Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi nonVCL Kapselung (https://www.delphipraxis.net/101492-nonvcl-kapselung.html)

Neutral General 14. Okt 2007 12:40


nonVCL Kapselung
 
Hi,

Das ist jetzt meine Ober-Frage zu dem Thema. Ich stell hier in letzter Zeit ungefähr 10 nonVCL Fragen am Tag. Jetzt komm ich ma konkret zum Punkt weil ich langsam die Schn.... voll hab. :evil:

Also wie ihr vielleicht schon mitbekommen hab versuche ich sowas wie ne kleine eigene VCL zu bauen.

Dazu hab ich eine Basis-Klasse TNVCLControl. Es geht jetzt hauptsächlich um die Form (TNVCLForm).

Delphi-Quellcode:
// Die vorläufige TNVCLForm
TNVCLForm = class(TNVCLControl)
  private
    wc: TWndClassEx;
    FList: TControlList;
    FCanClose: Boolean;
    FCaption: String;
    FOnCreate: TCreateEvent;
    FOnDestroy: TNotifyEvent;
    FOnClose: TNotifyEvent;
    FOnCloseQuery: TCloseQueryEvent;
    FTButtons: TTitleButtons;
    FSizeable: Boolean;
    FIcon: HICON;
    procedure SetCaption(const Value: String);
    procedure SetTButtons(const Value: TTitleButtons);
    procedure SetSizeable(const Value: Boolean);
    procedure SetIcon(const Value: HICON);
  protected
    procedure WndProc(var Message: TMessage);
  public
    constructor Create(AParent: TNVCLControl); override;
    procedure Close;
    property Caption: String read FCaption write SetCaption;
    property TitleButtons: TTitleButtons read FTButtons write SetTButtons;
    property Sizeable: Boolean read FSizeable write SetSizeable;
    property Icon: HICON read FIcon write SetIcon;
    property Controls: TControlList read FList;
    destructor Destroy; override;
    property OnCloseQuery: TCloseQueryEvent read FOnCloseQuery write FOnCloseQuery;
    property OnClose: TNotifyEvent read FOnClose write FOnClose;
    property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
    property OnCreate: TCreateEvent read FOnCreate write FOnCreate;
  end;
Was jetzt wichtig ist, ist der constructor und der/die/das WndProc:

Delphi-Quellcode:
constructor TNVCLForm.Create(AParent: TNVCLControl);
var _Create: TMethod;
    l: Integer;
    resStr: String;
    a: WNDCLASS;
begin
  inherited Create(AParent);
  FList := TControlList.Create;
  // Standard Werte setzen
  FLeft := 50;
  FTop := 50;
  FHeight := 540;
  FWidth := 780;
  FSizeable := true;
  FTButtons := [tbClose, tbMinimize, tbMaximize];
 
  // OnCreate zuweise
  SetLength(resStr,High(Byte));
  l := LoadString(hInstance,0,@resStr[1],High(Byte));
  SetLength(resStr,l);
  LoadString(hInstance,0,@resStr[1],l+1);

  _Create.Code := MethodAddress(resStr);
  _Create.Data := Self;
  FOnCreate := TCreateEvent(_Create);

  // Neue Fensterklasse registrieren
  with wc do
  begin
    cbSize := SizeOf(TWndClassEx);
    lpfnWndProc := @WndProcDispatch;
    hInstance := SysInit.hInstance;
    hbrBackground := GetSysColorBrush(COLOR_BTNFACE);
    lpszClassName := Pchar('Form' + IntToStr(frmCount));
    hIcon := LoadIcon(0,IDI_APPLICATION);
    cbWndExtra := 4;
    hIconSm := hIcon;
  end;
  inc(frmCount);
  RegisterClassEx(wc);
  // Fenster erstellen
  FHandle := CreateWindowEx(0,wc.lpszClassName,wc.lpszClassName,
                            {WS_VISIBLE or }WS_CAPTION or WS_SYSMENU or WS_SIZEBOX or WS_MAXIMIZEBOX or WS_MINIMIZEBOX,
                            FLeft,FTop,FWidth,FHeight,HWND_DESKTOP,FID,hInstance,nil);
  SetWindowLong(FHandle,GWL_USERDATA,Integer(Self)); // Self an das Fenster hängen
  SendMessage(FHandle,WM_CREATE,0,0); // nachträglich aufs OnCreate reagieren weil ich das eigentliche durch den Dispatcher nicht mitbekomme.
  ShowWindow(FHandle,SW_SHOW); // Erst jetzt das Fenster zeigen.
end;
Dann kommen wir jetzt zu dem WndProc gedöns.

Delphi-Quellcode:
function WndProcDispatch(wnd: HWND; Msg: UINT; lp: LPARAM; wp: WPARAM): LResult; stdcall;
var Self: TNVCLForm;
    m: TMessage;
begin
  Integer(Self) := GetWindowLong(wnd,GWL_USERDATA); // Self bekommen
  if Self <> nil then
  begin
    m.Msg := Msg;
    m.WParam := wp;
    m.LParam := lp;
    m.Result := 0;
    Self.WndProc(m); // WndProc des aufrufenden Fensters starten
    if m.Result = -1 then // <---- 1)
      Result := DefWindowProc(wnd,Msg,lp,wp)
    else
      Result := m.Result;
  end
  else
    Result := DefWindowProc(wnd,Msg,lp,wp);
end;
Delphi-Quellcode:
procedure TNVCLForm.WndProc(var Message: TMessage);
var x: Pointer;
    ps: TPaintStruct;
     i: Integer;
begin
  Message.Result := 0;
  case Message.Msg of
    WM_LBUTTONUP: begin
                    if Assigned(FOnClick) then
                      FOnClick(Self);
                    SetFocus(0);
                  end;
    WM_COMMAND: begin
                  if hiWord(Message.WParam) = BN_CLICKED then // <--- 2)
                  begin
                    x := FList.Find(Message.WParamLo);
                    if Assigned(TNVCLControl(x)) then
                    begin
                      if Assigned(TNVCLControl(x).OnClick) then
                        TNVCLControl(x).OnClick(TNVCLControl(x));
                    end;
                  end;
                end;
    WM_CREATE: begin
                   if Assigned(FOnCreate) then
                     FOnCreate(Self);
                end;
    WM_CLOSE:  begin
                  FCanClose := true;
                  if Assigned(FOnCloseQuery) then
                    FOnCloseQuery(Self,FCanClose);
                  if FCanClose then
                  begin
                    if Assigned(FOnClose) then
                      FOnClose(Self);
                    Message.Result := -1; // <--- 1)
                  end
                  else
                    Message.Result := Integer(FCanClose)
                end;
    WM_PAINT : begin
                  BeginPaint(FHandle,ps);
                  for i := 0 to FList.Count - 1 do
                    TNVCLControl(FList[i]).DoPaint;
                  EndPaint(FHandle,ps);
                  Message.Result := -1; // <--- 1)
                end;
    WM_DESTROY: begin
                  if Assigned(FOnDestroy) then
                    FOnDestroy(Self);
                  for i := FList.Count - 1 downto 0 do
                    TNVCLControl(FList[i]).Free;
                  PostQuitMessage(0);
                end;
    else Message.Result := DefWindowProc(FHandle, Message.Msg, Message.WParam, Message.LParam);
  end;
end;
so ich habe Stellen mit 1) und 2) im Code markiert die ich erklären will.

1) Wenn ich im WndProc der Klasse DefWindowProc aufrufe (außer beim dem else :gruebel: ) dann stürzt das Programm mit einem Runtime-Error ab. Keine Ahnung wieso. Deswegen setze ich Result nur auf -1 und rufe dann im Dispatcher wieder DefWindowProc auf.

2) Seltsamerweise bekomme ich wenn ich auf nen Button klicke die WM_COMMAND Message aber mit total falschen Parametern, sodass letztendlich doch nichts passiert. Das verstehe ich nicht.

Man muss dazu sagen das ich zuvor ein WndProc benutzt habe das ich mithilfe von Luckies (?) MakeProcInstance(m: TMethod) in einen normalen Funktionszeiger umgewandelt habe und der Fensterklasse übergeben habe. Jetzt habe ich auf diesen Dispatcher umgestellt weils meiner Meinung einfach eleganter ist. Früher lief aber alles ohne Probleme. Jetzt funktioniert zumindest die WM_COMMAND Message nicht mehr.

Das letzte Problem was ich aber auch schon früher hatte, ist das ich keine zwei Forms erstellen kann. Die zweite Form wird einfach nicht erstellt. CreateWindowEx liefert als Handle eine 0. Ich dachte zuerst es läge an dem WndProc und auch deshalb hab ich auf den Dispatcher umgestellt aber es läuft so oder so nicht :(

Wenn im Verlaufe dieses Threads Antworten auf alle diese Probleme gefunden werden könnten wäre ich euch ewig dankbar :)

Gruß
Neutral General

Apollonius 14. Okt 2007 12:54

Re: nonVCL Kapselung
 
Zitat:

Das letzte Problem was ich aber auch schon früher hatte, ist das ich keine zwei Forms erstellen kann
Ich weiß nicht, ob es daran liegt, aber du solltest die Registrierung der Fensterklasse nicht im Konstruktor machen, denn so wird versucht, die Klasse zweimal zu registrieren. In .NET gibt es für so was den Klassenkonstruktor, in Win32 darfst du die irgendetwas aus den Fingern saugen.

Was ist denn TControlList.Find? Ohne das zu wissen wird es schwierig die Sache mit den "total falschen Parametern" zu klären.

Die Sache, warum das mit der DefWndProc abschmiert, kann ich mir überhaupt nicht erklären.

Neutral General 14. Okt 2007 13:04

Re: nonVCL Kapselung
 
TControlList ist eine Klasse die Pointer zu allen Child-Control eines Formulars speichert.

Delphi-Quellcode:
function TControllist.Find(id: HMENU): Pointer;
var i: Integer;
begin
  Result := nil;
  for i := 0 to Count-1 do
   if TNVCLControl(Items[i]).FID = id then
     Result := Items[i];
end;
Man übergibt die ID des Controls und erhält den Pointer auf selbiges. Und da im LoWord des WParams der WM_COMMAND Message die ID des Controls gespeichert ist ...normalerweise.. kann ich so herausfinden auf welchen Control geklickt wurde und dann eben das OnClick Ereignis auslösen. Das hatte damals auch wunderbar geklappt als ich noch nicht diesen Dispatcher benutzt habe.

Frage: Warum wird versucht die Klasse zweimal zu registrieren wenn man sie im Constructor registriert? Und ich meine: Beim ersten Formular funktionierts ja.

Gruß
Neutral General

Apollonius 14. Okt 2007 13:15

Re: nonVCL Kapselung
 
Ähm? Wenn du zwei Formulare erzeugst, rufst du auch zweimal den Konstruktor auf und versuchst zweimal, die Fensterklasse zu registrieren.

Neutral General 14. Okt 2007 13:38

Re: nonVCL Kapselung
 
Hast du dir das mal angeguckt?

Delphi-Quellcode:
constructor [...]
begin
  //
  with wc do
  begin
    lpszClassName := Pchar('Form' + IntToStr(frmCount));
  end;
  inc(frmCount);
  RegisterClassEx(wc);
  //
end;
Gruß
Neutral General

Apollonius 14. Okt 2007 13:44

Re: nonVCL Kapselung
 
Sorry, da habe ich gepennt. Aber trotzdem würde ich es nur einmal registrieren, da du sowieso immer die selben Attribute nimmst.

Neutral General 14. Okt 2007 13:57

Re: nonVCL Kapselung
 
Hi,

Jetzt wird das ganze nur einmal registriert. Ändert aber nichts. WM_Command kommt immernoch vollkommen falsch an -.- Und zwei Fenster erstellen kann ich immernoch nicht...

Gruß
Neutral General

Apollonius 14. Okt 2007 14:01

Re: nonVCL Kapselung
 
:gruebel: Hast du mal GetLastError ausgwewertet (das ist in letzter Zeit irgendwie meine Standardfrage :) )? Die MSDN sagt ja
Zitat:

This function typically fails for one of the following reasons:

an invalid parameter value
the system class was registered by a different module
the WH_CBT hook is installed and returns a failure code
the window procedure fails for WM_CREATE or WM_NCCREATE

Neutral General 14. Okt 2007 14:24

Re: nonVCL Kapselung
 
Die einzige Meldung die ich während des ganzen Create-Prozesses bekomme (ich habe alles mit GetLastError gepflastert :mrgreen: ) ist nachdem die Klasse registriert wird:

Zitat:

---------------------------
Message
---------------------------
The system cannot find the file specified
---------------------------
OK
---------------------------
Wobei aber der Rückgabewert von RegisterClassEx <> 0 ist. In nem anderen Thread wurde mir gesagt ich soll/kann das ignorieren.

Gruß
Neutral General

Apollonius 14. Okt 2007 14:27

Re: nonVCL Kapselung
 
Und nach dem fehlschlagenden CreateWindowEx? Null kann ich mir irgendwie nicht vorstellen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:34 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz