![]() |
Re: WndProc in Klasse
Tja ich hatte zwar schon mal geantwortet, aber irgendwie wurde es verschluckt.
Du benutzt eine globale Fensterprocedure als Dispatcher von fensterprocedure: stdcall zu Methode. Nachdem du im Object das Fensterhandle erzeugt hast muß mit GetWindowLong(gwl_WndProc) die originale Fensterprocedure geholt und im Object gespeichert werden. Danach wird mit SetProp() Self im fensterhandle gespeichert und anschließend mit SetWindowLong(gwl_WndProc, WndProcDispatcher) ein erneutes subclassing durchgeführt.
Delphi-Quellcode:
Gruß Hagen
var
SelfAtom: TAtom; function WndProcDispatcher(Wnd: hWnd; Msg,wParam,lParam: Integer): Integer; stdcall; var M: TMessage; Self: TMyObjct; begin M.Msg := Msg; M.wParam := wParam; M.lParam := lParam; M.Result := 0; Integer(Self) := GetProp(Wnd, SelfAtom); Self.WindowMethod(M); Result := M.Result; end; // unter Vorbehalt alles aus dem Gedächtnis, // für Headcrash oder 3. Weltkrieg wird also keine Vrantwortung übernommen PS: für den 2. Weg bastel ich noch ein Beispiel, da ich eigentlich diese preferiere |
Re: WndProc in Klasse
So hier die 2. Methode, wenns Fragen gibt WEIL es NICHT funktioniert dann wurschtel dich erstmal durch :)
Delphi-Quellcode:
Im obigen Beispiel wird vorrausgesetzt fas
type
TCallDispatcher = packed record POP_EAX: Byte; PUSH_CONST: Byte; Self: Pointer; PUSH_EAX: Byte; JMP_RELATIVE: Byte; Offset: Integer; end; procedure InitDispatcher(var Dispatcher: TCallDispatcher; Self, Method: Pointer); begin Dispatcher.POP_EAX := $58; Dispatcher.PUSH_CONST := $68; Dispatcher.Self := Self; Dispatcher.PUSH_EAX := $50; Dispatcher.JMP_RELATIVE := $E9; Dispatcher.Offset := PChar(Method) - PChar(@Dispatcher) - SizeOf(Dispatcher); end; type TMyObject = class FDispatcher: TCallDispatcher; FHandle: hWnd; function WndProc(Wnd: hWnd; Msg,wParam,lParam: Integer): Integer; stdcall; procedure CreateHandle; end; function TMyObject.WndProc(Wnd: hWnd; Msg,wParam,lParam: Integer): Integer; stdcall; begin end; procedure TMyObject.CreateHandle; // zur ansatzweisen Demonstration var Params: TCreateParams; begin // TControl.CreateParams(Params); InitDispatcher(FDispatcher, Self, @TMyObject.WndProc); Params.WindowClass.lpfnWndProc := @FDispatcher; // Windows.RegisterClass(); with Params do FHandle := CreateWindowEx(ExStyle, WinClassName, Caption, Style, X, Y, Width, Height, WndParent, 0, WindowClass.hInstance, Param); end; { Aufruf aus dem OS sieht so aus PUSH lParam PUSH wParam PUSH Msg PUSH Wnd CALL WndProc -> PUSH ReturnAddress JMP WndProc -> zeigt auf @FDispatcher WndProc.FDispatcher code ist POP EAX = ReturnAddress PUSH Self PUSH EAX JMP TMyObject.WndProc } procedure Test_WindowProc; var TestWndProc: function(Wnd: hWnd; Msg,wParam,lParam: Integer): Integer; stdcall; TestObject: TMyObject; begin TestObject := TMyObject.Create; try InitDispatcher(TestObject.FDispatcher, TestObject, @TMyObject.WndProc); TestWndProc := @TestObject.FDispatcher; TestWndProc(1, 2, 3, 4); finally TestObject.Free; end; end; // Beispiel für einen anderen CallDispatcher function TForm1.DoEnumWindows(Wnd: hWnd; Param: Integer): Bool; stdcall; begin end; procedure TForm1.Test_Enum; var Enum: TCallDispatcher; begin InitDispatcher(Enum, Self, @TForm1.DoEnumWindows); EnumWindows(@Enum, 0); end; 1.) TMyObject.WndProc eine statische Methode ist also NICH virtual oder dynamic 2.) TMyObject.WndProc stdcall und identisch mit einer normalen Fensterfunktion Der TCallDispatcher und InitDispatcher kann mit JEDER anderen STDCALL Callback umgehen. Z.b. also auch mit EnumWindows() ö.ä. Quatsch wie oben gezeigt. Für deine Zwecke wird also als Fensterprocedure deines Fenster die Adresse von @FDispatcher gesetzt. Achso eines noch: da ich aufzeigen wollte wie man mit dieser Methode von Anfang an JEDE Fenstermessage mitbekommt, also auch wm_Create, wm_NCCreate usw. hat sie eine nicht unwesentliche Schwäche. Da für jedes Object das auf diese Weise arbeitet eine eigene Fensterfunktion die individuell ist existiert, wird auch jedesmal eine eigene Fensterklasse benötigt. D.h. JEDESMAL muß eine eigene Fensterklasse registriert werden. Dies ist natürlich für die Mehrfache Erzeugung des gleichen Objectes inakzeptabel. Dann muß man schon ähnliche Wege gehen wie es Borland macht. Als Start-Fensterfunction wird eine globale Procedure benutzt die nur EINMAL durch wm_Create aufgerufen wird. Diese Funktion mappt dann über GetProp() gespeicherte Werte auf obigen FDispatcher jeweils individuell, mit SetWindowLong(gwl_WndProc,...), Fertig. Bei Borland heist diese Prozedur "StdWndProc". Um es perfekt zu machen musst du also mein letztes Posting mit dem Trick in diesem Posting kombinieren. :firejump: Gruß Hagen |
Re: WndProc in Klasse
Netter Trick mit TCallDispatcher. Nur solltest du noch sicherstellen, das der Speicherbereich auch ausführbar ist. Ein VirtualProtect wäre also angebracht.
Delphi-Quellcode:
procedure InitDispatcher(var Dispatcher: TCallDispatcher; Self, Method: Pointer);
var OldProtect: Cardinal; AlignedAddress: Cardinal; begin Dispatcher.POP_EAX := $58; Dispatcher.PUSH_CONST := $68; Dispatcher.Self := Self; Dispatcher.PUSH_EAX := $50; Dispatcher.JMP_RELATIVE := $E9; Dispatcher.Offset := PChar(Method) - PChar(@Dispatcher) - SizeOf(Dispatcher); if IsBadCodePtr(@Dispatcher) then begin // @Dispatcher auf Speicher-Seite ausrichten (4 KB) AlignedAddress := Cardinal(@Dispatcher) and $FFFFF000; VirtualProtect(Pointer(AlignedAddress), 4096, PAGE_EXECUTE_READWRITE, OldProtect); end; end; |
Re: WndProc in Klasse
Gute Feststellung. Ich habe dies aber absichtlich nicht erwähnt da ich noch nie Probleme damit hatte das irgendein Speicherbereich nicht auch ausführbar war. Soviel ich weiß sind normales Codesegement, Stacks und durch Borland MM angelegter Speicher immer ausführbar.
Ich lasse mich aber gerne vom Gegenteil überzeugen :)) Schwieriger wird es dann schon zu erklären warum eine so erzeugte "Callback" Funktion die ja in unserem Prozessbezogenen lokalen Speicher liegt denoch aus anderen Prozessen heraus dispatcht werden können ;) Gruß Hagen |
Re: WndProc in Klasse
Zitat:
Zitat:
|
Re: WndProc in Klasse
Jedes Processmodul bekommt seinen eigenen virtuellen Speicherbereich. Fordern wir einen Speicherblock an, zB. ein Object mit FDispatcher, so ist die Speicheradresse individuell auf den Process bezogen. D.h. zwei Prozesse mit unseren TMyObject könnten rein theoretisch ein Object allozieren das aus unserer Sicht and der gleichen Adresse im Speicher liegt und denoch an unterschiedlicher Adresse im physikalischen Speicher. Eben ein Feature von Protected Mode Systemen.
Nun installieren wir eine Fensterprocedure die ja auf unsere virtuell Speicheradresse zeigt. Aber diese Fensterfunktion kann Prozessübergreifend benutzt werden. Das macht das OS für uns per CallWindowProc() API. Aber interessant ist es allemale, da ich immer wieder von naiven Versuchen von Programmierern höre die mit GetWindowLong(gwl_Wndproc) versuchen aus Fensterhandles die zu anderen Prozessen gehören die Fensterprocedure zu lesen und direkt aufzurufen !! Aber aus deiner Antwort entnehme ich das dir das schon klar sein dürfte :) Gruß Hagen |
Re: WndProc in Klasse
Zitat:
|
Re: WndProc in Klasse
Ah, die zweite Methode ist das, was ich wollte. Habe es jetzt noch nicht ausprobiert, das werd ich gleich tun. Danke auch für das Beispiel der ersten Methode, auch wenn das nicht erforderlich war, da ich das dank deiner Erklärung und dem SDK selbst zusammengebastelt habe.
|
Re: WndProc in Klasse
So, ich hab mir das jetzt mal angekuckt.
In der Form kann ich das leider nicht gebrauchen, da das Parent-Control bei mir schon existieren muss. Ich erklär mal kurz, wie das funktionieren soll: Die Klasse THexListView ist - wie der Name schon sagt - ein Listview, das als HexViewer funktioniert. Dieses wird dann entweder in der Nachrichtenschleife einer nonVCL-Anwendung oder aber irgendwo in einer VCL-Anwendung erzeugt. Da ich also nicht weiß, in welcher "Umgebung" die Klasse funktionieren soll, will ich die Klasse autonom gestalten, sodass die Nachrichtenschleife des übergeordneten Objektes nicht verändert werden muss. Dafür subclasse ich die Nachrichtenschleife ja in der HexView-Klasse. Ich will nun nichts weiter tun, als in der Klasse die Nachrichten an das Parent zu verarbeiten, bevor das Parent selbst sie verarbeitet. |
Re: WndProc in Klasse
Naja dann musst das Parent.Handle eben subclassen. Dein ListView Control enthält dann eine ParentWndProc() und ein FParentDispatcher. Mit SetWindowLong(Paran.Handle, GWL_WNDPROC, @FParentDispatcher) subclasst du dann.
Gruß Hagen |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:37 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz