2

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()
{

}
shadeglare
  • 7,006
  • 7
  • 47
  • 59
  • possible duplicate of [Best method for storing this pointer for use in WndProc](http://stackoverflow.com/questions/117792/best-method-for-storing-this-pointer-for-use-in-wndproc) – legends2k Jun 24 '15 at 09:59

1 Answers1

4

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.

lapk
  • 3,838
  • 1
  • 23
  • 28
  • Borland's VCL framework gets around this issue by dynamically allocating a block of executable memory to serve as a WndProc proxy function. The target object pointer is stored within this proxy. When the proxy is called by the OS, the proxy retreives the object pointer and calls a virtual method on it to process the message. This way, the object pointer is ready to go even before the first message is received (the object pointer is stored in the HWND for other purposes, but not for message dispatching). Messages are dispatched from the OS directly to the object without involving the HWND. – Remy Lebeau Dec 01 '11 at 22:49
  • @RemyLebeau-TeamB That's a neat way to deal with callbacks in general, but isn't it a bit too heavyweight in this case? It requires knowledge of calling conventions, knowledge of compiler conventions with regard to 'this' pointer and, possibly, virtual tables, use of platform-dependent assembler, more memory allocated with `VirtualAlloc()` per object (several pointers and assembler instructions, at least) and, possibly, slower execution. – lapk Dec 02 '11 at 19:11
  • Also, with this specific case - window creation, I'm a bit confused with regard to how you would benefit from this technique at all. I cannot figure out a way to use your method without subclassing each window instance you create - you need to supply Windows with the address of your callback which resides in your dynamically allocated memory, different for each object. And you need `HWND` to subclass a window instance, meaning the earliest you can do it is in `WM_NCCREATE`. Or do you mean, there is a way to do it without subclassing each window instance? How?! – lapk Dec 02 '11 at 19:11
  • the topic of this discussion is object-oriented message dispatching. Since the OS WndProc callbacks have no concept of objects, SOME kind of proxy is needed to pass a received message to an object method. Whether that proxy is a plain WndProc function assigned by `RegisterClass()` or `SetWindowLong(GWL_WNDPROC)` and the object pointer is stored inside the `HWND` by `SetWindowLong(GWL_USERDATA)`, or the proxy is dynamic and stores the object pointer inside of itself, the result is the same. The OS calls the proxy directly, then the proxy calls the object. – Remy Lebeau Dec 02 '11 at 20:24
  • what I described may be heavier to implement in code, but it is not slow (if anything, it is a very fast), and there are techniques to manage the virtual memory efficiently (Borland uses reusable pools of allocated proxies to reduce the number of allocations needed). Yes, if you want a generic implementation that works across compilers, it can be a bit involved (I know, I wrote a cross-compiler object-to-callback library that uses the dynamic proxy approach). – Remy Lebeau Dec 02 '11 at 20:27
  • Borland does the subclassing to assign its proxy by using `RegisterClass()` to register an intermediate WndProc function that gets called on the first message received. That function uses `SetWindowLong(GWL_WNDPROC)` to install the allocated message proxy into the `HWND` and set up other attributes of the `HWND` (most notably, to call `SetProp()` to set up some VCL-specific markers). All subsequent messages then go through the proxy. – Remy Lebeau Dec 02 '11 at 20:34
  • @RemyLebeau-TeamB I agree - for message dispatching execution speed is the same. We both agree that, ultimately, one needs to store some information about object in window instance - object pointer itself or pointer to proxy function that has object pointer inside it. However, proxy method has several times higher memory overhead, code complexity and compiler/processor dependency, while giving no benefits whatsoever. The first Windows window message has to be processed explicitly to setup pointers in both scenarios. Proxy is a general way to deal with callbacks, but it's not needed here. – lapk Dec 02 '11 at 21:20
  • I guess, Borland used proxy as general way to deal with callbacks and just applied it everywhere. I suppose, for large library development it's the way to go. – lapk Dec 02 '11 at 21:21
  • I would not say that using a dynamically allocated proxy has no benefit at all. There are situations where it does comes in handy. Most notably, when using a library/API does does NOT allow you to pass any application data around to callbacks (an `HWND` WndProc does, by storing the app data inside the `HWND` itself). In that case, a dynamic proxy is useful to avoid having to store your app data in the global scope. As for Borland's proxy, it is not used everywhere, it is specific to its object-oriented window message dispatching only. – Remy Lebeau Dec 02 '11 at 22:19