undefined identifier GWL_USERDATA

Koroskin Denis 2korden at gmail.com
Wed Jul 2 05:44:01 PDT 2008


On Wed, 02 Jul 2008 00:05:36 +0400, Jim Gadrow <makariverslund at gmail.com>  
wrote:

> Jim Gadrow Wrote:
>
>>
>> That solves the problem of the undefined identifier. I'm assuming  
>> that's the value assigned in the lib?
>>
>> I'm still experiencing the error, but now that I at least have the  
>> correct value for GWL_USERDATA it's probably an error in my code  
>> somewhere. Maybe I need to use CreateWindowEx instead or something. If  
>> I solve it I'll post just to close this thread :)
>
> Well, I found a solution that actually doesn't involve converting  
> pointers!
>
> I declare a static dynamic array of the class I wanted to store a  
> pointer to at the beginning of the WndProc.
>
> Then, I use the GetWindowLongA function to retrieve any index supplied  
> with the HWND.
>
> Then, upon window creation, I resize the array and add an instance of  
> the class. I record the index at which the class was created and stuff  
> just a good ol' fashioned int into the WindowLong (which requires no  
> casting!).
>
> Since the index is retrieved before the switch statement is entered, I  
> can simply do: Ctrl[index].Paint () afterwards!
>
> Anyone see a flaw with this method? Let me know as I've been wracking my  
> brain for HOURS trying to find a solution that works.
>

I think that most of the people use one of the two tricks: store a  
HashMap!(HWND, Control) for HWND->Control mapping or set some user data  
via window's GWL_USERDATA. Just use proper casting to and from integer,  
otherwise you may end up with a null pointer.

Since you succeeded with a custom HWND->Control mapping, I'll show you the  
second way.

All you have to do is set a pointer to your (base) class via SetWindowLong  
method. Note that it is prefferred to use SetWindowLongPtr for 64bit  
compatibility. I use it exclusively and have never had any problems. Do as  
the follows:

         HWND hWnd = CreateWindow(...);
         assert (hWnd != HWND.init);

         SetWindowLongPtr(hWnd, GWLP_USERDATA, cast(long)cast(void*)this);
// or   SetWindowLong(hWnd, GWL_USERDATA, cast(int)cast(void*)this);

Next, once you are in a WndProc method, you should do invert operation:

         long userData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
// or   int userData = GetWindowLong(hWnd, GWL_USERDATA);

         if (userData != 0) {
             void* ptr = cast(void*)userData;
             if (auto wnd = cast(MyWindow)ptr) {
                 // success!
                 return wnd.processEvent(hWnd, message, wParam, lParam);
             }
         }

And here is a full source, just copy-and-paste it to run. Tested on both  
DMD1 and DMD2 compilers.

Just a small note: it uses third-party win32 bindings, not phobos ones.  
Those headers that come with Phobos are old, incomplete and badly ported,  
can't wait when they will be replaced with a new ones. You can get them  
 from dsource:
http://www.dsource.org/projects/bindings/browser/trunk/win32 - trunk  
browser
http://www.dsource.org/projects/bindings/changeset/263/trunk?old_path=%2F&format=zip  
- download whole repo

In any case, it should be fairy simple to use phobos headers instead.

Hope that helps.

Hello.d
~~~~~~~

import win32.windows;
import std.string;
import std.stdio;

pragma(lib, "gdi32.lib");

class MyWindow
{
     extern(Windows) static LRESULT WndProc(HWND hWnd, UINT message, WPARAM  
wParam, LPARAM lParam)
     {
         long userData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
         if (userData != 0) {
             void* ptr = cast(void*)userData;
             if (auto wnd = cast(MyWindow)ptr) {
                 return wnd.processEvent(hWnd, message, wParam, lParam);
             }
         }

         return DefWindowProc(hWnd, message, wParam, lParam);
     }

     bool registerClass() {
         WNDCLASSEX wcex;

         wcex.cbSize = WNDCLASSEX.sizeof;
         wcex.style			= CS_HREDRAW | CS_VREDRAW;
         wcex.lpfnWndProc	= &WndProc;
         wcex.cbClsExtra		= 0;
         wcex.cbWndExtra		= 0;
         wcex.hInstance		= GetModuleHandle(null);
         wcex.hIcon			= HICON.init;
         wcex.hCursor		= HCURSOR.init;
         wcex.hbrBackground	= cast(HBRUSH)(COLOR_WINDOW+1);
         wcex.lpszMenuName	= null;
         wcex.lpszClassName	= cast(char*)toStringz(className);
         wcex.hIconSm		= HICON.init;

         return RegisterClassEx(&wcex) != 0;
     }

     bool createWindow() {
         HWND hWnd = CreateWindow(cast(char*)toStringz(className),  
cast(char*)toStringz(title), WS_OVERLAPPEDWINDOW,
           CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HANDLE.init, HANDLE.init,  
GetModuleHandle(null), null);

         if (hWnd == HWND.init) {
             return false;
         }

         SetWindowLongPtr(hWnd, GWLP_USERDATA, cast(long)cast(void*)this);

         ShowWindow(hWnd, SW_SHOW);
         UpdateWindow(hWnd);

         return true;
     }

     LRESULT processEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM  
lParam) {

         // here should better be some mapping from message to a delegate,  
like this:
         // auto dg = message in handlersMap;
         // if (dg !is null) return dg(hWnd, message, wParam, lParam);
         // return DefWindowProc(hWnd, message, wParam, lParam);

         switch (message)
         {
         case WM_PAINT:
             {
                PAINTSTRUCT ps;
                RECT rect;

                HDC hDC = BeginPaint(hWnd, &ps);
                GetClientRect(hWnd, &rect);

                HBRUSH hbr = cast(HBRUSH) GetStockObject(BLACK_BRUSH);
                FillRect(hDC, &rect, hbr);
                EndPaint(hWnd,&ps);
             }
             break;

         case WM_DESTROY:
             PostQuitMessage(0);
             break;

         default:
             return DefWindowProc(hWnd, message, wParam, lParam);
         }

         return 0;
     }

     int doMainLoop() {
         MSG msg;
         while (GetMessage(&msg, null, 0, 0))
         {
             TranslateMessage(&msg);
             DispatchMessage(&msg);
         }

         return cast(int) msg.wParam;
     }

     string className = "MyWindow";
     string title = "MyCaption";
}

int main() {
     auto myWindow = new MyWindow();
     if (!myWindow.registerClass()) {
         writefln("failed to registerClass");
         return 1;
     }

     if (!myWindow.createWindow()) {
         writefln("failed to createWindow");
         return 2;
     }

     return myWindow.doMainLoop();
}



More information about the Digitalmars-d mailing list