Subclassing a window with a functor (Win32)

Quick sanity check: is it possible to subclass a window using a functor? I am facing a situation where I want to get some data in win proc, but GWLP_USERDATA is already in use. The functor seems like a good alternative, but I'm having trouble getting it to work.

Here's the basics:

class MyWinProc { // Win Proc Functor
public:
    MyWinProc(ExternalClass* obj, HWND window) :
                obj(obj), window(window) {
                oldWinProc = SubclassWindow(window, this); // Apply Subclass
            }

    virtual ~MyWinProc() {
                SubclassWindow(window, oldWinProc); // Remove Subclass
            }

    LRESULT CALLBACK operator()(HWND, UINT, WPARAM, LPARAM) {
                switch( uMsg ) {
        case WM_MOUSEMOVE: {
            obj->onMouseMove(/*etc*/);
            break;
        }
                }
                return CallWindowProc(oldWinProc, hWnd, uMsg, wParam, lParam);
            }

private:
    ExternalClass* obj;
    HWND  window;
    WNDPROC oldWinProc;
};

      

Everything seems to be good and good, but when I hit DispatchMessage () in me the pump message, I have "Access Writing Location 0x00000000" obviously not a good sign. Remove the call to the above code and life will be happy again. :( So it's even possible, or am I not mistaken about it?

+2


source to share


5 answers


GWLP_USERDATA is not the only way to store window related data, you can also use SetProp () .



And at least on x86 you can use ATL type thunking (a little bit of asm code that puts your class pointer in ecx and then jumps to your wndproc). You can find some links about this in the answer I posted here

+3


source


The CALLBACK function must be a static member function or otherwise a direct C-style function. The Windows API doesn't know anything about C ++ objects.

Something like this should work:



class MyWinProc { 
public:
        MyWinProc(ExternalClass* obj, HWND window) :
                obj(obj), window(window) {
                pContext = this;

                oldWinProc = SubclassWindow(window, &MyWinProc::wndproc); // Apply Subclass
            }

        virtual ~MyWinProc() {
                SubclassWindow(window, oldWinProc); // Remove Subclass
            }


private:
        static MyWinProc* pContext;

        static
        LRESULT CALLBACK wndproc( HWND, UINT, WPARAM, LPARAM) {
            MyWndProc& me = *pContext;

            // do your WndProc work...
        }

        ExternalClass* obj;
        HWND  window;
        WNDPROC oldWinProc;
};

      

+8


source


The problem with using a functor is the call: Windows expects the address to be the address of a static function and will use / call that address as such; whereas the 'this' you are passing is not the address of a static function.

Windows will use an address like this (pseudo-encoded assembly):

; push the necessary parameters
push [hWnd]
push etc...
; invoke the specified address (of the static function)
call [callback]

      

To call the functor, the Windows code should be like this:

; push the necessary parameters
push [hWnd]
push etc...
; invoke the specified address (of the functor object)
; ... first, put the 'this' pointer as a hidden parameter into the ecx register
mov ecx,[callback]
; ... next, invoke the address (where is it?) of the class' functor method
call MyWinProc::operator()

      

... or instead of the last two operators, the following operators if the operator is virtual ...

; ... first, put the 'this' pointer as a hidden parameter into the ecx register
mov ecx,[callback]
; ... next, invoke the address of the operator via an (which?) entry
;     in the class' vtable
call [ecx+8]

      

None of these are possible because O / S is unaware of the calling conventions for C ++ non-static methods, especially including:

  • Method of passing implicit parameter 'this'
  • Address of non-virtual class methods
  • Vtable elements of virtual class methods
+5


source


GWLP_USERDATA is already in use

I don't know what your SubclassWindow function is, but CWnd :: SubclassWindow says "The window should not be attached to an MFC object when this function is called."

I am facing a situation where I want to get some data in win proc

The usual (non-MFC) way of implementation, which is to have a global / static dictionary, whose key / index is the HWND value for Windows subclasses, and whose data is the data you want to associate with this window: this data is often this

C ++ class pointers ...

You subclass the window procedure with your static callback function: your static callback function then, when called, uses the HWND it passed in to look up the data in the static dictionary.

+3


source


You can still use the value stored in GWLP_USERDATA ...

class MyWinProc { // Win Proc Functor
public:
MyWinProc(ExternalClass* obj, HWND window) :
  obj(obj), window(window) {
      oldUserData = GetWindowLongPtr(GWLP_USERDATA);
      oldWinProc = SubclassWindow(window, this); // Apply Subclass
  }

  virtual ~MyWinProc() {
      SubclassWindow(window, oldWinProc); // Remove Subclass
  }

  LRESULT CALLBACK operator()(HWND, UINT, WPARAM, LPARAM) {       
      switch( uMsg ) {
            case WM_MOUSEMOVE: {
                obj->onMouseMove(/*etc*/);
                break;
                }
      }
      LONG userDataToRestore = SetWindowLongPtr(GWLP_USERDATA, oldUserData);
      LRESULT lRet = CallWindowProc(oldWinProc, hWnd, uMsg, wParam, lParam);
      SetWindowLongPtr(GWLP_USERDATA, userDataToRestore);
  }

private:
ExternalClass* obj;
HWND  window;

LONG oldUserData;
WNDPROC oldWinProc;
};

      

-1


source







All Articles