Delphi Object + WndProc

Started by
8 comments, last by mikeman 17 years, 9 months ago
In VC++ I have been able to redirect Window Messages (via Wndproc) to an instance of a class's specific message handler by storing the instance address with SetWindowLong. Found this detailed on the net somewhere. Worked Perfectly. Does anyone know why the same idea doesnt work (as I have it) in Delphi? At the moment, if I try to change a variable within the class the program just dies - no errors. It executes code with the class's message handler, but doesnt seem to like variable access. Code Follows: This is the main WndProc associated with the WndClass. As you can see, it retrieves a pointer with GetWindowLong, and if its not NULL it jumps to the class's own message handler, otherwise just passes it to DefWindowProc. This part of the code seems to execute quite happily. In another part of the code is a SetWindowLong which stores a pointer to 'self'.

function WndProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM):LRESULT; stdcall;
var
  wndobj: pGLEngine;
begin
  wndobj:= pGLEngine(ptr(GetWindowLong(hWnd, GWL_USERDATA)));
  if wndobj = nil then
    result:= DefWindowProc(hWnd, uMsg, wParam, lParam)
  else
		result:= wndobj.MsgHandler(hWnd, uMsg, wParam, lParam)
end;
This is the class's 'internal' message handler. The problem occurs within the function. All the code seems to execute, as long as I don't touch a class variable. The case WM_ACTIVATE causes an error when it trys to set Active:=.... Active is declared in the private section of the GLEngine class. If I {..} that line out, its fine. If I add something trivial like 'Active:= true', the program dies without error. I have tested other variables as well that are within the class - all cause the program to die.

function GLEngine.MsgHandler(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM):LRESULT;
begin
  case uMsg of
    WM_ACTIVATE:
    begin
      if (hi(wParam)=0) then active:= true else active:= false;
      result:= 0;
      exit;
    end;
    WM_SYSCOMMAND:
    begin
      if (wParam = SC_SCREENSAVE) or (wParam = SC_MONITORPOWER) then
      begin
        result:= 0;
        exit;
      end;
    end;
...
...
end;
Is there a trick to delphi's classes when it comes to variables? [Edited by - Sentient2k5 on July 4, 2006 3:02:59 PM]
Advertisement
I don't know much about delphi. But if you want your code to be readable...

Enclose it in either {code}{/code} or {source}{/source} brackets. (replace the squiggly-braces with square-braces)
Seems that the problem isn't with the code itself.

I changed the code to store the pointer in a global variable, and it works perfectly. Infact, with the old code, it works fine for a short while then seems to corrupt the data stored with SetWindowLong.

Does anyone know if Delphi stores data to GWL_USERDATA with SetWindowLong? This is the only reason I can see why it would change as I only have one call to SetWindowLong, and its in the WindowCreate function which is called only once in my program - I can only think Delphi is changing this data behind the scenes.
Probably. Just don't use GWL_USERDATA. In the WNDCLASS structure, there is the cbWndExtra member that allows you to allocate memory of N bytes(in your case,4 bytes). Then, you can use SetWindowLong/GetWindowLong with a value of 0..N-1(in your case,0) that specifies the offset to the value that is to be read/written.
Quote:Original post by Sentient2k5
Seems that the problem isn't with the code itself.

I changed the code to store the pointer in a global variable, and it works perfectly. Infact, with the old code, it works fine for a short while then seems to corrupt the data stored with SetWindowLong.

Does anyone know if Delphi stores data to GWL_USERDATA with SetWindowLong? This is the only reason I can see why it would change as I only have one call to SetWindowLong, and its in the WindowCreate function which is called only once in my program - I can only think Delphi is changing this data behind the scenes.


Are you registering your own window class with RegisterClass() or RegisterClassEx()?

You should only use the GWL_USERDATA area if and only if you are registering your own because you cannot guarantee that the window manager will not use that space in other classes of windows and dialogs.
- CheeseMonger
Grr, I now think this is a pointer problem.

Basically, I have GLEngine = class, and pGLEngine = ^GLEngine.

If I create a global variable MyGLE:GLEngine, and set MyGLE = self within the GLEngine.CreateWindow procedure, the code runs without problems.

If I create a global variable MyGLEP:pGLEngine, and set MyGLEP = @self, then it dies.

Help Please :)
I don't know Delphi, but from what I've been reading, I get the impression that "self" is already a pointer, like the "this" pointer in C++. This would mean that "@self" would return a pointer to a pointer, which is probably not what you want and could be the cause of an access violation somewhere.
- CheeseMonger
In Delphi, when you declare "MyGLE:GLEngine", MyGLE is already a pointer. It's not like C++, where you can do:

GLEngine myGLE;
myGLE.foo();

or

GLEngine *myGLE=new GLEngine();
myGLE->foo();

In Delphi, objects are always created dynamically on the heap, and the Create() constructor returns a reference to them:

var
myGLE:GLEngine;
alias:GLEngine;
...
myGLE=GLEngine.Create();
alias=myGLE;//both alias and myGLE reference the same object
alias.foo();

You can also see that sizeof(GLEngine) returns "4", since it is a pointer. If you want to get the size of the object, you must do GLEngine.InstanceSize();

So there isn't any reason to get a pointer to a pointer. Plus, "self" is a local variable, so @self will be illegal outside of the scope of the particular function.
Quote:Plus, "self" is a local variable, so @self will be illegal outside of the scope of the particular function.


Thanks for this Mikeman. Clears up the problem completely. I'm just confused as to why it runs through ~100 messages before it decides to die. Not that its a problem anymore. Cheers.
Quote:
I'm just confused as to why it runs through ~100 messages before it decides to die.


Doesn't matter. Storing the address of a local variable and using it outside of its scope is illegal and yields what is called "undefined behaviour". Maybe it will crash, maybe it will run until the address in which the local variable was stored gets overwritten(probably your case) and then crash, maybe you'll corrupt other unrelated data, maybe your computer will blow up[smile]. Ok, probably not the last one, but when you're dealing with illegal pointers you never really know for sure what's going to happen.

This topic is closed to new replies.

Advertisement