Best method for storing this pointer for use in WndProc

前端 未结 10 784
南旧
南旧 2020-11-29 20:06

I\'m interested to know the best / common way of storing a this pointer for use in the WndProc. I know of several approaches, but each as I underst

相关标签:
10条回答
  • 2020-11-29 20:43

    While using the SetWindowLongPtr and GetWindowLongPtr to access the GWL_USERDATA might sound like a good idea, I would strongly recommend not using this approach.

    This is the exactly the approached used by the Zeus editor and in recent years it has caused nothing but pain.

    I think what happens is third party windows messages are sent to Zeus that also have their GWL_USERDATA value set. One application in particular was a Microsoft tool that provied an alternative way to enter Asian characters in any windows application (i.e. some sort of software keyboard utility).

    The problem is Zeus always assumes the GWL_USERDATA data was set by it and tries to use the data as a this pointer, which then results in a crash.

    If I was to do it all again with, what I know now, I would go for a cached hash lookup approach where the window handle is used as the key.

    0 讨论(0)
  • 2020-11-29 20:43

    You should use GetWindowLongPtr()/SetWindowLongPtr() (or the deprecated GetWindowLong()/SetWindowLong()). They are fast and do exactly what you want to do. The only tricky part is figuring out when to call SetWindowLongPtr() - You need to do this when the first window message is sent, which is WM_NCCREATE.
    See this article for sample code and a more in-depth discussion.

    Thread-local storage is a bad idea, since you may have multiple windows running in one thread.

    A hash map would also work, but computing the hash function for every window message (and there are a LOT) can get expensive.

    I'm not sure how you mean to use thunks; how are you passing around the thunks?

    0 讨论(0)
  • 2020-11-29 20:44

    I recommend setting a thread_local variable just before calling CreateWindow, and reading it in your WindowProc to find out the this variable (I presume you have control over WindowProc).

    This way you'll have the this/HWND association on the very first message sent to you window.

    With the other approaches suggested here chances are you'll miss on some messages: those sent before WM_CREATE / WM_NCCREATE / WM_GETMINMAXINFO.

    class Window
    {
        // ...
        static thread_local Window* _windowBeingCreated;
        static thread_local std::unordered_map<HWND, Window*> _hwndMap;
        // ...
        HWND _hwnd;
        // ...
        // all error checking omitted
        // ...
        void Create (HWND parentHWnd, UINT nID, HINSTANCE hinstance)
        {
            // ...
            _windowBeingCreated = this;
            ::CreateWindow (YourWndClassName, L"", WS_CHILD | WS_VISIBLE, x, y, w, h, parentHWnd, (HMENU) nID, hinstance, NULL);
        }
    
        static LRESULT CALLBACK Window::WindowProcStatic (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
        {
            Window* _this;
            if (_windowBeingCreated != nullptr)
            {
                _hwndMap[hwnd] = _windowBeingCreated;
                _windowBeingCreated->_hwnd = hwnd;
                _this = _windowBeingCreated;
                windowBeingCreated = NULL;
            }
            else
            {
                auto existing = _hwndMap.find (hwnd);
                _this = existing->second;
            }
    
            return _this->WindowProc (msg, wparam, lparam);
        }
    
        LRESULT Window::WindowProc (UINT msg, WPARAM wparam, LPARAM lparam)
        {
            switch (msg)
            {
                // ....
    
    0 讨论(0)
  • 2020-11-29 20:44

    In the past I've used the lpParam parameter of CreateWindowEx:

    lpParam [in, optional] Type: LPVOID

    Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) pointed to by the lParam param of the WM_CREATE message. This message is sent to the created window by this function before it returns. If an application calls CreateWindow to create a MDI client window, lpParam should point to a CLIENTCREATESTRUCT structure. If an MDI client window calls CreateWindow to create an MDI child window, lpParam should point to a MDICREATESTRUCT structure. lpParam may be NULL if no additional data is needed.

    The trick here is to have a static std::map of HWND to class instance pointers. Its possible that the std::map::find might be more performant than the SetWindowLongPtr method. Its certainly easier to write test code using this method though.

    Btw if you are using a win32 dialog then you'll need to use the DialogBoxParam function.

    0 讨论(0)
提交回复
热议问题