In Win32 API a window has the pointer to a user defined version of WndProc function that handling its messages.
There are some ways to cover this low-level mechanism with a solution like MFC message map and so on.
In my very little app I'm looking for a way to encapsulate this low-level thing with a object oriented solution.
I tried to create a C++ map with HWND key and "MyWindowClass" item and when I created an object of the MyClass I added a pair to the map and then looking for the MyWindowClass object by HWN. But the problem is after CreateWindowEx called Win32 internally send a WM_CREATE message to the just created window so I can't add pair in the map before this message and can't control WM_CREATE through passing it to the object instanced WndProc.
The code is:
#ifndef NOTIFYWINDOW_H
#define NOTIFYWINDOW_H
#include "Bacn.h"
class NotifyWindow
{
private:
HWND m_hWnd;
Gdiplus::Graphics* m_graphics;
protected:
static std::map<HWND, NotifyWindow*> s_NotifyWindows;
static LRESULT CALLBACK s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static void s_WndMessageLoop();
LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
void Initialize();
void OnPaint();
void OnCreate();
public:
NotifyWindow();
~NotifyWindow();
};
#endif //NOTIFYWINDOW_H
And its implementation:
#include "NotifyWindow.h"
using namespace Gdiplus;
using namespace std;
map<HWND*, NotifyWindow> NotifyWindow::s_NotifyWindows;
LRESULT CALLBACK NotifyWindow::s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
map<HWND, NotifyWindow*>::iterator search = s_NotifyWindows.find(hWnd);
if (search != s_NotifyWindows.end())
{
search->second->WndProc(uMsg, wParam, lParam);
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void NotifyWindow::s_WndMessageLoop()
{
}
LRESULT NotifyWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
OnCreate();
break;
case WM_PAINT:
OnPaint();
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SIZE:
break;
case WM_SETFOCUS:
break;
case WM_KILLFOCUS:
break;
case WM_MOUSEMOVE:
break;
default:
return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}
return 0;
}
void NotifyWindow::Initialize()
{
WNDCLASSEX wc;
const wchar_t *className = L"BacnNotifyWindowClass";
const wchar_t *windowName = L"BacnNotifyWindow";
HINSTANCE hInstance = GetModuleHandle(NULL);
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpszClassName = className;
wc.lpfnWndProc = NotifyWindow::s_WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = NULL;
wc.hIconSm = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
RegisterClassEx(&wc);
DWORD dwExtStyle = WS_EX_TOPMOST;
DWORD dwStyle = WS_POPUP | WS_SYSMENU;
m_hWnd = CreateWindowEx(
dwExtStyle,
className,
windowName,
dwStyle,
300,
300,
100,
100,
NULL,
NULL,
hInstance,
NULL);
s_NotifyWindows.insert(pair<HWND, NotifyWindow*>(m_hWnd, this));
ShowWindow(m_hWnd, SW_SHOW);
}
NotifyWindow::NotifyWindow()
{
Initialize();
}
NotifyWindow::~NotifyWindow()
{
}
void NotifyWindow::OnPaint()
{
}
void NotifyWindow::OnCreate()
{
}
Suggestion: make
WndProc
virtual in your window base class. Request Windows to allocate extra memory for each window instance big enough to store a pointer (usecbWndExtra
). When creating a window, put a pointer to your windows class object into this extra memory associated with each window instance usingSetWindowLongPtr
. In yourstatic sWndProc
, retrieve this pointer usingGetWindowLongPtr
and call your window base class functionvirtual WndProc
. In my opinion, it's more neat way than having a whole extra map object dedicated to dispatchingWndProc
calls.EDIT: Plus, you do realize, in your code you are trying to register Windows window class every time you create an object of your window class? If you create just one window this is technically fine, I guess, but even then it's error-prone design. Windows window class should be registered just once, not every time you create a window with this Windows window class.
EDIT: Also, in your code, if you are not processing Windows message in your
WndProc
, you code will callDefWindowProc
twice for this message: first time in member function withinswitch
clause, second time instatic sWndProc
.DefWindowProc
shouldn't be called twice on the same message.EDIT: Sorry, I missed your actual question before somehow, I thought your post was about design, not about
WM_CREATE
. In order to processWM_NCCREATE
,WM_NCCALCSIZE
orWM_CREATE
in a uniform way, you can setlpParam
in call toCreateWindowEx
to, again, pointer to the object of your window class. This parameter will be passed to yourstatic sWndProc
as a member ofCREATESTRUCT
withWM_NCCREATE
andWM_CREATE
. You can process, say,WM_NCCREATE
(first message to be sent) insidestatic sWndProc
, get this pointer to your object, useSetWindowLongPtr
to put it into window instance extra memory, and then use it to call member functionWndProc
(just be careful about calling not fully created object of your windows class, if you callCreateWindowEx
from its constructor). This way, you don't need to worry about "low-level" Windows message dispatching anywhere else in your program. You, of course, will still need your member functionWndProc
to dispatch messages to actual function calls.