Reputation: 7536
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()
{
}
Upvotes: 2
Views: 3408
Reputation: 3918
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 (use cbWndExtra
). When creating a window, put a pointer to your windows class object into this extra memory associated with each window instance using SetWindowLongPtr
. In your static sWndProc
, retrieve this pointer using GetWindowLongPtr
and call your window base class function virtual WndProc
. In my opinion, it's more neat way than having a whole extra map object dedicated to dispatching WndProc
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 call DefWindowProc
twice for this message: first time in member function within switch
clause, second time in static 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 process WM_NCCREATE
, WM_NCCALCSIZE
or WM_CREATE
in a uniform way, you can set lpParam
in call to CreateWindowEx
to, again, pointer to the object of your window class. This parameter will be passed to your static sWndProc
as a member of CREATESTRUCT
with WM_NCCREATE
and WM_CREATE
. You can process, say, WM_NCCREATE
(first message to be sent) inside static sWndProc
, get this pointer to your object, use SetWindowLongPtr
to put it into window instance extra memory, and then use it to call member function WndProc
(just be careful about calling not fully created object of your windows class, if you call CreateWindowEx
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 function WndProc
to dispatch messages to actual function calls.
Upvotes: 4