Thema: Delphi WndProc in Klasse

Einzelnen Beitrag anzeigen

Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#22

Re: WndProc in Klasse

  Alt 18. Jul 2003, 23:19
So hier die 2. Methode, wenns Fragen gibt WEIL es NICHT funktioniert dann wurschtel dich erstmal durch

Delphi-Quellcode:
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;
Im obigen Beispiel wird vorrausgesetzt fas
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.

Gruß Hagen
  Mit Zitat antworten Zitat