Procedura obsługi okna w klasie

0

Umieszczam procedurę obsługi okna w klasie. Ponieważ chcę mieć dostęp do zmiennych i metod klasy. Niestety procedura jeżeli chce być dodana jako pointer na funkcje do struktury WNDCLASSEX to procedura jeżeli jest w klasie musi być statyczna. Niestety metody statyczne mogą używać tylko zmiennych i metod statycznych. Myślałem o zmianie wszystkich funkcji i zmiennych na statyczne, ale to przyniosło kolejne problemy. Dobrym pomysłem było też użycie funkcji zaprzyjaźnionych, ale procedura obsługi okna przyjmuje cztery parametry i piątego nie przyjmie. Jak mógł bym rozwiązać ten problem ?

0

Tak na szybko, pisane z palca:

#include <map>


class Window
{
private:
	static std::map<HWND, Window*> m_hwnd_window;


	static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		Window*	_this;
	
	
		if(uMsg == WM_CREATE)
		{			
			m_hwnd_window[hwnd] = _this = (Window*)((CREATESTRUCT*)lParam)->lpCreateParams;
			...
		}
		else
		{
			std::map<HWND, Window*>::iterator it;
			it = m_hwnd_window.find(hwnd);
			if(it == m_hwnd_window.end())return 0;
			_this = it->second;
			
			if(uMsg == WM_DESTROY)
			{
				m_hwnd_window.erase(it);
			}
		}
	
		
		return _this->WndProc(hwnd, uMsg, wParam, lParam);
	}
	
	LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		/* jesteśmy w domu ;)*/
	}
	
public:
}; 

Przy tworzeniu okna w ostatnim parametrze CreateWindowEx (lpParam) dajesz wskaźnik na instancje klasy Window

0

Inny sposób to wykorzystać cbWndExtra z WNDCLASS - ustawić na sizeof(Window*), i za pomocą SetWindowLongPtr z parametrem GWLP_USERDATA 0 zapisać tam this (albo nasz obiekt).
Odczyt wskaźnika przez GetWindowLongPtr, rzutowanie na nasze Window i jest.

Przykładu w tej chwili nie mam, ale kiedyś tak robiłem i działało.

0
Azarien napisał(a):

Przykładu w tej chwili nie mam, ale kiedyś tak robiłem i działało.

Ja mam przykład:

// ******** TWORZENIE OKNA **********
W przykładzie klasa która zawiera okno to "Main_D11", u ciebie klasa nazywa się jakoś inczej

ILU_RESULT ILU_CALL Main_D11::create()
{
  if(main_out == NULL) return ILU_NULLPASSED;

  // creation win32 window
  const TCHAR* const ILU_SystemName = TEXT("ILU_EngineSystem_Win32Class"); // win32 window's class name
  const uint32 stl = WS_DLGFRAME | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX; // win32 window basic style
  ::HWND hwnd;
  ::HINSTANCE hInst = GetModuleHandle(NULL); // get this *.exe image pointer
  ::WNDCLASSEX wcl = { sizeof(wcl), 0, Main::onWin32Event, 0, sizeof(Main_D11*), // << -- TUTAJ sizeof(Main_D11*) to co Cię interesuje !!!, pewnie masz 0
                     hInst, ::LoadIcon(NULL, IDI_APPLICATION), ::LoadCursor(NULL, IDC_ARROW),
                     (HBRUSH)::GetStockObject(BLACK_BRUSH), NULL, ILU_SystemName, NULL };
  ::RegisterClassEx(&wcl); // try register win32 wnd class
  hwnd = ::CreateWindowEx(0, ILU_SystemName, TEXT("..."), stl, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, hInst, NULL);
  if(hwnd == NULL) return ILU_APIERRORRETURNED; // something is wrong
  ::SetWindowLongPtr(hwnd, 0, this); // save object in win32 window extra memory <<-- TUTAJ zapisujesz this
  return ILU_OK;
}


// *********** PROCEDURA Main::onWin32Event która jest static w klasie ************

::LRESULT __stdcall Main_D11::onWin32Event(::HWND hwnd, unsigned int msg, ::WPARAM wParam, ::LPARAM lParam)
{
  // declare and get This pointer from HWND extra memory
  #define  ILU_GetThis(_hwnd_)    This = reinterpret_cast<Main_D11*>(::GetWindowLongPtrW(_hwnd_, 0))
  Main* This;
  /* przez tego Thisa odwołujesz się do składowych obiektu klasy, przy każdym obsługiwanym messagu,
      którzy będziesz przetwarzał dane klasy, musisz go pobrać */

  switch(msg) { // win32 events identifiers
  
  //mouse events
  case WM_MOUSEMOVE:
    ILU_GetThis(hwnd); // <<-- pobranie i niżej uzycie
    if(This->events_sys.onMouseMove.is_bind())
      This->events_sys.onMouseMove(Vector2(static_cast<float>(LOWORD(lParam)), static_cast<float>(HIWORD(lParam))));
    return 0;

  // key events
  case WM_KEYDOWN: ILU_GetThis(hwnd); if(This->events_sys.onKeyDown.is_bind()) This->events_sys.onKeyDown(wParam); return 0;
  case WM_KEYUP:   ILU_GetThis(hwnd); if(This->events_sys.onKeyUp.is_bind())   This->events_sys.onKeyUp(wParam);   return 0;

  // onClose event request
  case WM_CLOSE:
    ILU_GetThis(hwnd);
    if(This->events_sys.onClose.is_bind())
      if(This->events_sys.onClose() == true)
        ILU_ConvertToInterface(IMain, This)->stop(); // stop if client want
    return 0; // do nothing by default
  
  default: return ::DefWindowProc(hwnd, msg, wParam, lParam); // all other events
  }
}

A jeśli w całym programie tworzysz tylko 1 okno, zapamiętaj sobie this globalnie i jego używaj w procedurze obsługi, aby odwoływać się do składowych obiektu klasy. Nie ma sensu utrudniać sobie jeśli nie musisz. Te oba powyższe przykłady są rozwiązaniem problemu w przypadku, gdy istnieje możliwość tworzenia wielu instancji okien, a więc i obiektów.

0
// *********** PROCEDURA Main::onWin32Event która jest static w klasie ************
 ::LRESULT __stdcall Main_D11::onWin32Event(::HWND hwnd, unsigned int msg, ::WPARAM wParam, ::LPARAM lParam)

Ja bym zrobił to tak:

// static
 ::LRESULT __stdcall Main_D11::onWin32EventStatic(::HWND hwnd, unsigned int msg, ::WPARAM wParam, ::LPARAM lParam)
{
  Main* This = (Main_D11*)GetWindowLongPtr(hwnd,0);
  return This->onWin32Event(hwnd, msg, wParam, lParam);
}

// *********** PROCEDURA Main::onWin32Event która NIE jest static w klasie ************
::LRESULT __stdcall Main_D11::onWin32Event(::HWND hwnd, unsigned int msg, ::WPARAM wParam, ::LPARAM lParam)
{
  // i tu już normalnie, bez żadnych GetThis.
}
0

Też tak zrobiłem na początku, ale po chwili zdałem sobie sprawę, że taki This to jakaś, (acz oczywiście nie duża) oszczędność cykli, bo w praktyce będziesz obsługiwał 1% messagów (przy dobrych wiatrach), a 99% będzie leciało do DefWindowProc (są programy które np. co jakiś czas broadcastują po wszystkich oknach jakieś dodatkowe własne message'y, po kiego?), GetThis'em wołasz GetWindowLongPtr tylko wtedy, kiedy jest to faktycznie potrzebne, czyli w 1% przychodzących messagów.
Oczywiście pewnie GetWindowLongPtr + dodatkowy call nie ma jakiegoś narzutu i nie odbije się na wydajności... ale to taka powiedzmy drobniutka optymalizacja :>

1 użytkowników online, w tym zalogowanych: 0, gości: 1