52875.fb2 Creating a Win32 Window Wrapper Class - читать онлайн бесплатно полную версию книги . Страница 3

Creating a Win32 Window Wrapper Class - читать онлайн бесплатно полную версию книги . Страница 3

The Window Class

Windows in Win32 are represented by window handles , so the logical first object to encapsulate is the window handle. Anybody who has tried to wrap window classes knows that you can not initialize the window class with a class method as the window procedure unless it is a static method, so the class also needs a static "message router" that determines which instance of the class is the recipient of the message and calls that instance's window procedure. So how exactly do we determine which instance should receive the message?

Every window has a 32-bit value associated with it intended for use by the application. This value can be set via the SetWindowLong function and retrieved with the GetWindowLong , and this is the core of the message routing technique.

LRESULT CALLBACK Window::MsgRouter(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {

 Window *wnd = 0;

 // retrieve associated Window instance

 wnd = reinterpret_cast<Window *>(::GetWindowLong(hwnd, GWL_USERDATA));

 // call the windows message handler

 wnd->WndProc(message, wparam, lparam);

}

While this looks perfect, where and when was this value stored? At first I stored it during my Window::Create routine, but I ran into problems. You see, some messages are sent before CreateWindowEx returns – WM_NCCREATE, WM_NCCALCSIZE and WM_CREATE (in that order). If the value does not exist by the time WM_NCCREATE is called then, by some mysterious Windows behavior that I still don't understand, the value never gets inserted. The solution? The WM_NCCREATE message must be handled explicitly within the message router, with the object pointer being stored at this point instead. Okay, but where do we get the value from? The CreateWindowEx function allows the passing of a (void) pointer to window creation data. This data is made available to the window procedure as the lparam parameter in the form of an LPCREATESTRUCT. As an added precaution, we go ahead and store the window handle at this point.

LRESULT CALLBACK Window::MsgRouter(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {

 Window *wnd = 0;

 if (message == WM_NCCREATE) {

  // retrieve Window instance from window creation data and associate

  wnd = reinterpret_cast<Window *>((LPCREATESTRUCT)lparam)->lpCreateParams;

  ::SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<long>(wnd));

  // save window handle

  wnd->SetHWND(hwnd);

 } else

  // retrieve associated Window instance

  wnd = reinterpret_cast<Window *>(::GetWindowLong(hwnd, GWL_USERDATA));

 // call the windows message handler

 wnd->WndProc(message, wparam, lparam);

}