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