Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Object oriented version of windows message procedure callback ?

108 views
Skip to first unread message

Skybuck Flying

unread,
Sep 27, 2009, 8:28:57 PM9/27/09
to
Hello,

I am stuck with the following code:

function WndProc(HWindow: HWND; message, WParam, LParam: Longint): Longint;
stdcall;

wc: WNDCLASS;
wc.lpfnWndProc := @WndProc;

This is known as the "windows message procedure" (callback).

I would like to make the wndproc part of Twindow object so that it might be
possible to create multiple windows by creating multiple Twindow objects...
?

The common way to do this would be to use some kind of field to store the
object reference into it.

Then the wndproc would call the object window procedure something like this:

function WndProc( Etc );
begin
// typecast somefield/reference to object to Twindow
TWindow(Etc.SomeField).WindowProcedure; // call the oo version.
end;

This most be a common problem for object orientated programmers, so I am
trying to save some time by asking you in case you already solved this
problem.

Some questions:

What would be a good field to use ? Is this possible at all ?

Or does it need a different solution ?

(I can vaguely remember something about "CreateInstance" ?)

Any advice is welcome !

(Visual Studio C++ programmers probably face the same problem too so I add
that newsgroup too... maybe they know a solution ! ;) I will also dive into
the Delphi code... to see if I can see a solution in there ! ;))

Bye,
Skybuck.


Skybuck Flying

unread,
Sep 27, 2009, 8:41:41 PM9/27/09
to
Hmm... Delphi (2007) seems to use something quite complex...

It also seems to have a "window limit of 313" that's kinda funny... and why
is it 313 ? ;) :)

It also has a special StdWndProc which is probably the "procedural" version
which calls into the object oriented vesion.

I am kinda surprised that there is no virtualprotect call anywhere ?

The again maybe virtual protect is not needed when writing into it's own
virtual memory space ? Hmm a bit weird... since for dll's that would be
necessary ?!?

Maybe it doesn't actually overwrite any instructions...

Maybe it just executes extra instructions... See "BlockCode"..

But doesn't it need "execute" rights for that ?

Ok now I understand it a bit better, it apperently already gets execute
rights from the virtual alloc call:
"PAGE_EXECUTE_READWRITE"

I wonder if there might be an easier way... like overwriting the window
handle and using that as object reference... window handle could be stored
in object itself...

{$IFDEF MSWINDOWS}

const
InstanceCount = 313;

{ Object instance management }

type
PObjectInstance = ^TObjectInstance;
TObjectInstance = packed record
Code: Byte;
Offset: Integer;
case Integer of
0: (Next: PObjectInstance);
1: (Method: TWndMethod);
end;

type
PInstanceBlock = ^TInstanceBlock;
TInstanceBlock = packed record
Next: PInstanceBlock;
Code: array[1..2] of Byte;
WndProcPtr: Pointer;
Instances: array[0..InstanceCount] of TObjectInstance;
end;

var
InstBlockList: PInstanceBlock;
InstFreeList: PObjectInstance;

{ Standard window procedure }
{ In ECX = Address of method pointer }
{ Out EAX = Result }

function StdWndProc(Window: HWND; Message, WParam: Longint;
LParam: Longint): Longint; stdcall; assembler;
asm
XOR EAX,EAX
PUSH EAX
PUSH LParam
PUSH WParam
PUSH Message
MOV EDX,ESP
MOV EAX,[ECX].Longint[4]
CALL [ECX].Pointer
ADD ESP,12
POP EAX
end;

{ Allocate an object instance }

function CalcJmpOffset(Src, Dest: Pointer): Longint;
begin
Result := Longint(Dest) - (Longint(Src) + 5);
end;

function MakeObjectInstance(Method: TWndMethod): Pointer;
const
BlockCode: array[1..2] of Byte = (
$59, { POP ECX }
$E9); { JMP StdWndProc }
PageSize = 4096;
var
Block: PInstanceBlock;
Instance: PObjectInstance;
begin
if InstFreeList = nil then
begin
Block := VirtualAlloc(nil, PageSize, MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
Block^.Next := InstBlockList;
Move(BlockCode, Block^.Code, SizeOf(BlockCode));
Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2],
@StdWndProc));
Instance := @Block^.Instances;
repeat
Instance^.Code := $E8; { CALL NEAR PTR Offset }
Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
Instance^.Next := InstFreeList;
InstFreeList := Instance;
Inc(Longint(Instance), SizeOf(TObjectInstance));
until Longint(Instance) - Longint(Block) >= SizeOf(TInstanceBlock);
InstBlockList := Block;
end;
Result := InstFreeList;
Instance := InstFreeList;
InstFreeList := Instance^.Next;
Instance^.Method := Method;
end;

{ Free an object instance }

procedure FreeObjectInstance(ObjectInstance: Pointer);
begin
if ObjectInstance <> nil then
begin
PObjectInstance(ObjectInstance)^.Next := InstFreeList;
InstFreeList := ObjectInstance;
end;
end;

var
UtilWindowClass: TWndClass = (
style: 0;
lpfnWndProc: @DefWindowProc;
cbClsExtra: 0;
cbWndExtra: 0;
hInstance: 0;
hIcon: 0;
hCursor: 0;
hbrBackground: 0;
lpszMenuName: nil;
lpszClassName: 'TPUtilWindow');

function AllocateHWnd(Method: TWndMethod): HWND;
var
TempClass: TWndClass;
ClassRegistered: Boolean;
begin
UtilWindowClass.hInstance := HInstance;
{$IFDEF PIC}
UtilWindowClass.lpfnWndProc := @DefWindowProc;
{$ENDIF}
ClassRegistered := GetClassInfo(HInstance, UtilWindowClass.lpszClassName,
TempClass);
if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then
begin
if ClassRegistered then
Windows.UnregisterClass(UtilWindowClass.lpszClassName, HInstance);
Windows.RegisterClass(UtilWindowClass);
end;
Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
'', WS_POPUP {+ 0}, 0, 0, 0, 0, 0, 0, HInstance, nil);
if Assigned(Method) then
SetWindowLong(Result, GWL_WNDPROC, Longint(MakeObjectInstance(Method)));
end;

procedure DeallocateHWnd(Wnd: HWND);
var
Instance: Pointer;
begin
Instance := Pointer(GetWindowLong(Wnd, GWL_WNDPROC));
DestroyWindow(Wnd);
if Instance <> @DefWindowProc then FreeObjectInstance(Instance);
end;


Skybuck Flying

unread,
Sep 27, 2009, 8:56:09 PM9/27/09
to
Ok,

The wnd handle is controlled/create and managed by windows so overwriting
this would not be possible.

For now I can see one easy way to create an object oriented version,
something I did when injecting into winsock.

The simple idea would be to use a stub...

So each window object would create it's own procedural windows message
procedure which then calls it's object oriented version...

So this would require allocating some memory, making it "executeable",
placing the procedural instruction codes there... and then making the
assembler call the oo version.

One small drawback is that it would require a little bit more memory than
just one procedural thingy... but a few bytes per Twindow seems more then
acceptable nowadays ! ;)

The only remaining question/problem could be with calling the oo version...

Does anything special need to happen me wonders ?!?

Would Delphi do anything special after the call... like cleaning up ?!?

Hmm...

Now I go read a tutorial which I found... let's see what this guy has though
of:

http://www.codeproject.com/KB/winsdk/win32windowwrapperclass.aspx?msg=635542

Bye,
Skybuck ! ;)


Skybuck Flying

unread,
Sep 27, 2009, 9:09:33 PM9/27/09
to
Ok,

The dude of the tutorial uses the following parameter of the CreateWindow
call:

LPVOID lpParam // pointer to window-creation data

I was already looking at the parameter and I wonder if it's save to do
that/use that parameter for garbage data....

Windows might expect a structure to be there...

Also commenters have said his code contained 32 to 34 memory leaks... I
haven't looked at his full source code yet... so I just take a look at it...
doesn't immediatly become apperently what these would be... probably some
cleanup issue's or so...

Usually any memory leaks are cleanup automatically be windows when
applications terminate so doesn't have to be much of an issue... could be
interesting to enable memory leak detection when I make my own example to
see if there is any memory leakage ;) :)

There might be another possibility, instead of using CreateWindow maybe this
one could be used:

CreateMDIWindow( etc )

The documentation for lParam says:

"
lParam

Specifies an application-defined value.
"

^ That's just what I need... kinda strange that CreateWindow except
something else there ?

Is it a documentation issue's or is it a real issue ? and is MDI the same
thing ?

Hmm...

Bye,
Skybuck.

Skybuck Flying

unread,
Sep 27, 2009, 9:15:07 PM9/27/09
to
Ok,

I have seen mdi applications long ago... I think it's something where there
is one main window... and all the sub windows are constricted to the main
window... so they cannot be outside the window...

I am also not sure if an MDI application first needs to create a main window
? ;)

Wikipedia has some interesting discussion about MDI applications:

http://en.wikipedia.org/wiki/Multiple_document_interface

Bye,
Skybuck.


Skybuck Flying

unread,
Sep 27, 2009, 9:39:37 PM9/27/09
to
Also the dude from the codeproject links to this article which also explains
some intricacies:

http://www.gamedev.net/reference/articles/article1810.asp

Now I only wonder why Delph's VCL does something much more complex ? ;)

(Probably for some advanced features or so, or maybe it's unnecesarilly
complex ? I don't know... maybe it's so Delphi can support MDI applications
?!? ;))

Bye,
Skybuck :)


Skybuck Flying

unread,
Sep 28, 2009, 12:53:21 AM9/28/09
to
Ok,

So far my Twindow seems to be working.

No drawing going on yet, just an empty/garbage window with resizing
possibilities and such.

I also tested if it was possible to create two windows and it's indeed
possible... but each window must have a different title during creation
otherwise it won't work ?! I found that a little bit weird really but ok.
(Without a different title Windows.RegisterClass would fail for second
window! ;)

Bye,
Skybuck.

Skybuck Flying

unread,
Sep 28, 2009, 2:30:05 AM9/28/09
to
Hmm this might not be necessary anymore...

The gpgpu example is now working thanks to some "dirty/cheap" vcl tricks !
;)

Though getting a decent Twindow myself might be nice sometime...

Maybe for now I use this "dirty/cheap" vcl trick for further projects...

Until I have something good working and then maybe later switch to Twindow
stuff...

This way I can concentrate more on the interesting gpgpu stuff.

Though I am not yet sure what the effect of the pixelformat of the vcl's
canvas will have on performance...

Maybe it's same pixelformat... I should check that out later somewhat, yeah
small sigh.

Many me flipping between windows api, vcl stuff, delphi stuff, c++ stuff,
direct x considerations (not gonna use direct x), opengl stuff, cg stuff,
cgfx stuff, nvidia stuff fx composer 2.5, documents stuff and then ofcourse
my own gpgpu algorithms.

Especially this "detour" into internal windows workings has cut into my time
and right now I feel confused ! ;) :)

Where the hell was I ? ;) :) LOL. Oh yeah now I remember... getting texture
mapping/loading working ! ;)

Gjez ! ;)

Bye,
Skybuck.


Skybuck Flying

unread,
Sep 28, 2009, 2:32:54 AM9/28/09
to
Oh yeah... now I remember...

I had to choose a good "(internal) pixel format ?"...

And I wanted to do a benchmark first...

Now I am still confused...

No integer benchmarks yet... and the formats are confusing me... what's it
all about ?!

Maybe I should ask a question... on the graphics forms...

But I still would like benchmarks in case answers are wrong oh well ?!

Bye,
Skybuck.


0 new messages