www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - undefined identifier GWL_USERDATA

reply Jim Gadrow <james.gadrow stmediagroup.com> writes:
Ok, I'm trying to create a windows application and I am experiencing an error
when storing and retrieving a pointer from a window long.

I receive the 'Error: undefined identifier GWL_USERDATA' message when compiling
unless I add:

extern (Windows) int GWL_USERDATA;

The program compiles correctly. However, when I attempt to restore the object
from the long (pointer) my application terminates. I checked and the value of
GWL_USERDATA is equal to 0 (the default int value). Thus, the program is NOT
storing the pointer in the correct location. I think 0 actually corresponds to
the GWL_EXSTYLE does it not?

Here's a modified version of winsamp.d that illustrates the error:

/* Compile with:
 *	dmd winsamp gdi32.lib winsamp.def
 */

import std.c.windows.windows;
import std.c.stdio;

const int IDC_BTNCLICK = 101;
const int IDC_BTNDONTCLICK = 102;

class control
{
	void Paint () {}
}

extern (Windows)
{
	uint GetWindowLongA (HWND hWnd, int nIndex);
	uint SetWindowLongA (HWND hWnd, int nIndex, LONG dwNewLong);
	int GWL_USERDATA;
}

T WinGetLong (T) (HWND hWnd, int which = GWL_USERDATA)
{
	return cast (T) GetWindowLongA (hWnd, which);
}

void WinSetLong (T) (HWND hWnd, T value, int which = GWL_USERDATA)
{
	SetWindowLongA (hWnd, which, cast (long) value);
}

extern(Windows)
int WindowProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		control pCtrl = cast (control) WinGetLong!(control*) (hWnd);
		
		case WM_CREATE:
			control Ctrl = new control ();
			WinSetLong!(control*) (hWnd, &Ctrl);
			break;
		
		
	    case WM_COMMAND:
	    {
		switch (LOWORD(wParam))
		{
		    case IDC_BTNCLICK:
			if (HIWORD(wParam) == BN_CLICKED)
				MessageBoxA(hWnd, "Hello, world!", "Greeting",
					MB_OK | MB_ICONINFORMATION);
			break;
		    case IDC_BTNDONTCLICK:
			if (HIWORD(wParam) == BN_CLICKED)
			{
			    MessageBoxA(hWnd, "You've been warned...", "Prepare to GP fault",
				    MB_OK | MB_ICONEXCLAMATION);
			    *(cast(int*) null) = 666;
			}
			break;
		}
		break;
	    }

	    case WM_PAINT:
	    {
		pCtrl.Paint ();
		static char[] text = "D Does Windows";
		PAINTSTRUCT ps;
		HDC dc = BeginPaint(hWnd, &ps);		
		RECT r;
		GetClientRect(hWnd, &r);
		HFONT font = CreateFontA(80, 0, 0, 0, FW_EXTRABOLD, FALSE, FALSE,
			FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
			DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial");
		HGDIOBJ old = SelectObject(dc, cast(HGDIOBJ) font);
		SetTextAlign(dc, TA_CENTER | TA_BASELINE);
		TextOutA(dc, r.right / 2, r.bottom / 2, text.ptr, text.length);
		SelectObject(dc, old);
		EndPaint(hWnd, &ps);
		break;
	    }
	    case WM_DESTROY:
		PostQuitMessage(0);
		break;

	    default:
		break;
	}
	return DefWindowProcA(hWnd, uMsg, wParam, lParam);
}

int doit()
{
	HINSTANCE hInst = GetModuleHandleA(null);
	WNDCLASS wc;
	wc.lpszClassName = "DWndClass";
	wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = &WindowProc;
	wc.hInstance = hInst;
	wc.hIcon = LoadIconA(cast(HINSTANCE) null, IDI_APPLICATION);
	wc.hCursor = LoadCursorA(cast(HINSTANCE) null, IDC_CROSS);
	wc.hbrBackground = cast(HBRUSH) (COLOR_WINDOW + 1);
	wc.lpszMenuName = null;
	wc.cbClsExtra = wc.cbWndExtra = 0;
	auto a = RegisterClassA(&wc);
	assert(a);
	
	HWND hWnd, btnClick, btnDontClick;
	hWnd = CreateWindowA("DWndClass", "Just a window", WS_THICKFRAME |
		WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE,
		CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, HWND_DESKTOP,
		cast(HMENU) null, hInst, null);
	assert(hWnd);
	
	btnClick = CreateWindowA("BUTTON", "Click Me", WS_CHILD | WS_VISIBLE,
		0, 0, 100, 25, hWnd, cast(HMENU) IDC_BTNCLICK, hInst, null);

	btnDontClick = CreateWindowA("BUTTON", "DON'T CLICK!", WS_CHILD | WS_VISIBLE,
		110, 0, 100, 25, hWnd, cast(HMENU) IDC_BTNDONTCLICK, hInst, null);

	MSG msg;
	while (GetMessageA(&msg, cast(HWND) null, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessageA(&msg);
	}
	
	return 1;
}

/**********************************************************/

/* Note the similarity of this code to the console D startup
 * code in \dmd\src\phobos\dmain2.d
 * You'll also need a .def file with at least the following in it:
 *	EXETYPE NT
 *	SUBSYSTEM WINDOWS
 */

extern (C) void gc_init();
extern (C) void gc_term();
extern (C) void _minit();
extern (C) void _moduleCtor();
extern (C) void _moduleUnitTests();

extern (Windows)
int WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
    int result;

    gc_init();			// initialize garbage collector
    _minit();			// initialize module constructor table

    try
    {
	_moduleCtor();		// call module constructors
	_moduleUnitTests();	// run unit tests (optional)

	result = doit();	// insert user code here
    }

    catch (Object o)		// catch any uncaught exceptions
    {
	MessageBoxA(null, cast(char *)o.toString(), "Error",
		    MB_OK | MB_ICONEXCLAMATION);
	result = 0;		// failed
    }

    gc_term();			// run finalizers; terminate garbage collector
    return result;
}
Jun 30 2008
parent reply torhu <no spam.invalid> writes:
Jim Gadrow wrote:
 Ok, I'm trying to create a windows application and I am experiencing an error
when storing and retrieving a pointer from a window long.
 
 I receive the 'Error: undefined identifier GWL_USERDATA' message when
compiling unless I add:
 
 extern (Windows) int GWL_USERDATA;
 
Try adding this instead of that: const GWL_USERDATA = -21;
Jun 30 2008
parent reply Jim Gadrow <makariverslund gmail.com> writes:
torhu Wrote:

 Try adding this instead of that:
 
 const GWL_USERDATA = -21;
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 :)
Jul 01 2008
parent reply Jim Gadrow <makariverslund gmail.com> writes:
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.
Jul 01 2008
parent "Koroskin Denis" <2korden gmail.com> writes:
On Wed, 02 Jul 2008 00:05:36 +0400, Jim Gadrow <makariverslund 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(); }
Jul 02 2008