Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Externes Fenster: DestroyWindow geht nicht


Steve1024 - Mi 15.06.11 23:02
Titel: Externes Fenster: DestroyWindow geht nicht

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
uses
  SysUtils, Classes, Forms, Messages, Controls, Windows;

type
  TExternalForm = class(TWinControl)
  private
    FExternalHWND: HWND;
  private // old data values
    FOldParent: HWND;
    FOldStyle,
    FOldExStyle: DWORD;
    FOldWndProc: Pointer;
  protected
    procedure CreateWnd; override;
    procedure DestroyWindowHandle; override;
    procedure SetParent(AParent: TWinControl); override;
  public
    constructor Create(AOwner: TComponent; AExternalHWND: HWND); reintroduce;
    procedure DefaultHandler(var Message); override;
    procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
    property ExternalHWND: HWND read FExternalHWND;
  end;

implementation

function OnEnumChildProc(hwWindow: HWND; lpParam: Integer): Bool; stdcall;
var vOwner: TExternalForm;
begin
  try
    vOwner := TExternalForm(Pointer(lpParam));
    with TExternalForm.Create(vOwner, hwWindow) do
    begin
      Parent := vOwner;
      TabOrder := 0;
    end;
  except (* do nothing on error *) end;
  Result := TRUE;
end;

{ TExternalForm }

constructor TExternalForm.Create(AOwner: TComponent; AExternalHWND: HWND);
begin
  inherited Create(AOwner);
  FExternalHWND := AExternalHWND;

  if (Windows.GetWindowLong(FExternalHWND, GWL_STYLE) AND WS_CLIPCHILDREN) <> 0 then
    ControlStyle := [csAcceptsControls, csDoubleClicks, csNoStdEvents]
  else ControlStyle := [csDoubleClicks, csNoStdEvents];
  TabStop := (Windows.GetWindowLong(FExternalHWND, GWL_STYLE) AND WS_TABSTOP) <> 0;
end;

procedure TExternalForm.CreateWnd;
var vParams: TCreateParams;
begin
  if FExternalHWND = 0 then
    raise Exception.Create('No external window found.');

  // backup current properties
  FOldParent := Windows.GetParent(FExternalHWND);
  FOldWndProc := Pointer(GetWindowLong(WindowHandle, GWL_WNDPROC));
  FOldStyle := Windows.GetWindowLong(FExternalHWND, GWL_STYLE);
  FOldExStyle := Windows.GetWindowLong(FExternalHWND, GWL_EXSTYLE);

  // set new mandatory values
  CreateParams(vParams);
  Windows.SetParent(FExternalHWND, GetParentHandle);
  Windows.SetWindowLong(FExternalHWND, GWL_STYLE,
    ((FOldStyle OR vParams.Style OR WS_CHILD) AND
    NOT (WS_POPUP OR WS_CAPTION)));
  Windows.SetWindowLong(FExternalHWND, GWL_EXSTYLE,
    ((FOldExStyle OR vParams.ExStyle OR WS_EX_TOOLWINDOW)));

  // control operations
  WindowHandle := FExternalHWND;
  DefWndProc := Pointer(GetWindowLong(WindowHandle, GWL_WNDPROC));
  CreationControl := Self;
  SetWindowLong(WindowHandle, GWL_WNDPROC, Longint(@InitWndProc));
  SendMessage(WindowHandle, WM_NULL, 00);

  // apply visibility
  if not Visible and IsWindowVisible(Handle) then
    ShowWindow(Handle, SW_HIDE);

  // do this for all child items too
  EnumChildWindows(FExternalHWND, @OnEnumChildProc, NativeInt(Self));
end;

const
  OCM_BASE = $2000;

procedure TExternalForm.DefaultHandler(var Message);
begin
  if HandleAllocated then
    with TMessage(Messagedo
    begin
      if (Msg >= CN_BASE) and (Msg < CN_BASE + WM_USER) then
        Msg := Msg - (CN_BASE - OCM_BASE);
      if not (csAcceptsControls in ControlStyle) then
      begin
        Result := CallWindowProc(DefWndProc, Handle, Msg, WParam, LParam);
        exit;
      end;
    end;

  inherited DefaultHandler(Message);
end;

procedure TExternalForm.DestroyWindowHandle;
begin
  inherited;
  // control operations
  SetWindowLong(WindowHandle, GWL_WNDPROC, Longint(DefWndProc));
  WindowHandle := 0;

  // restore backed-up properties
  Windows.SetWindowLong(ExternalHWND, GWL_WNDPROC, Longint(FOldWndProc));
  Windows.SetParent(ExternalHWND, FOldParent);
  Windows.SetWindowLong(ExternalHWND, GWL_STYLE, FOldStyle);
  Windows.SetWindowLong(ExternalHWND, GWL_EXSTYLE, FOldExStyle);

  CallWindowProc(FOldWndProc, ExternalHWND, WM_SYSCOMMAND, SC_CLOSE , 0);
  DestroyWindow(ExternalHWND);

  FExternalHWND := 0;
end;

procedure TExternalForm.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
  inherited;
  SetWindowPos(FExternalHWND, 0, ALeft, ATop, AWidth, AHeight,
    SWP_NOZORDER OR SWP_NOACTIVATE );
end;

procedure TExternalForm.SetParent(AParent: TWinControl);
var
  LRecreate: Boolean;
begin
  LRecreate := HandleAllocated;
  if LRecreate then
    UpdateRecreatingFlag(True);
  try
    if (Parent = niland LRecreate then
      DestroyHandle;
    inherited;
    if Parent <> nil then
      Windows.SetParent(ExternalHWND, GetParentHandle);
  finally
    if LRecreate then
      UpdateRecreatingFlag(False);
  end;
end;


Guten Abend Liebe Community,

ich hab mal wieder ein Anliegen. Nachdem ich nun die letzten zwei Tage folgenden Code zusammengestellt habe, bin ich nun in der Lage jede x-beliebige Windows Fenster in mein Programm einzubinden. Sogar TAB finkioniert eins A.

Allerdings muss ich dabei folgendes festellen:

a) DestroyWindow funktioniert nicht mehr, ebenso CloseWindow etc
b) Viselle Themes werden nicht angewendet. Trotz XPMan usw. Wenn ich das Fenster nicht einbinde, bzw das Control freigebe, wird das Fenster so angezeigt wie es sollte

Ansonsten funktioniert das sehr gut. Notepad oder gar Cmd hab ich schon in ein GroupBox eingebunen.
Jetzt hätte ich aber noch gerne, dass es so aussieht wie es soll und dass DestroyWindow klappt.

Hat mir von euch bitte einer eine Idee zu Verbesserung?
Danke!


Steve1024 - Fr 17.06.11 20:11

Weiss wirklich keiner Rat?


trm - Fr 17.06.11 21:33

Sendmessage sollte funktionieren.

SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);

Tipp für Win x64: http://www.delphipraxis.net/153735-64bit-sc_close.html

Gruß Mathias :)


Steve1024 - Fr 17.06.11 23:10

Hallo Mathias,
danke für deinen Tipp. Zum Einen habe ich Win7 64bit und zum Anderen funktioniert leider gar nichts.
Weder SendMessage, PostMessage noch der bereits vorhandene Code CallWindowProc(FOldWndProc, ExternalHWND, WM_SYSCOMMAND, SC_CLOSE , 0);.
Ich kann das Fenster nicht schließen und/oder zerstören. Ich bekomme auch keinen Fehlercode zurück. Erst beim zweiten Aufruf bekomme ich die Meldung, dass das Handle ungültig sei, zurück. Das wäre ja, wenn der erste Aufruf so funktioniert hätte wie er soll, auch richtig. Das Fenster wird durch eine DLL aus meinem eignen Programm erzeugt und kann daher wohl weniger ein Berechtigungsproblem sein.

Im Moment habe ich zudem kein 32bit System um es da zu testen.
Dennoch: Unter 64bit sollte es doch auch gehen, oder nicht?

PS: Das Ding ist in XE entwickelt. Ich hoffe daher doch, dass das HWND 64bit kompatibel ist, oder?
PS*: Zu Guter Letzt noch die Frage bzgl der Themes... Warum werden die Controls immer Nativ dargestellt?

Danke für die Hilfe!


trm - Fr 17.06.11 23:19

Probier Dein Tool mal als Admin zu starten.


Steve1024 - Fr 17.06.11 23:28

Bringt leider auch nichts. Es geht einfach nicht :(


Narses - Sa 18.06.11 12:23

Moin!

Bitte ändere den Titel des Topics, da er wenig über das eigentlich Thema verrät. Hier der entsprechende Absatz aus den Richtlinien [http://www.entwickler-ecke.de/richtlinien.html]:
1.2 Beiträge:
Bitte formuliere den Betreff Deiner Beiträge so, dass andere Mitglieder anhand dieser bereits das eigentliche Thema festmachen können. Beiträge wie etwa "Eine Anfängerfrage" oder "Weiß jemand, wie das geht?" lassen den Leser im Unklaren darüber, was das Thema der Diskussion ist.[...]
Einfach oben bei Deinem ersten Beitrag auf user defined image oder user defined image klicken und den Titel ändern. Danke Dir!

cu
Narses


Delphi-Laie - Sa 18.06.11 18:05

user profile iconSteve1024 hat folgendes geschrieben Zum zitierten Posting springen:
bin ich nun in der Lage jede x-beliebige Windows Fenster in mein Programm einzubinden.


Wozu?

Ich fändes es hilfreich, wenn Du wenigstens kurz den Zweck Deines Programmes nennen/vorstellen könntest/würdest.

user profile iconSteve1024 hat folgendes geschrieben Zum zitierten Posting springen:
Sogar TAB finkioniert eins A.


Dito: Wozu und außerdem noch: inwiefern?


Steve1024 - Mo 20.06.11 16:19

Der Zweck ist eigentlich, PlugIns zu entwickeln.
Dabei sollen diese in der Lage sein ein Fenster zu erzeugen was ich dann mittels des HWND in meine Anwenung integriere (zB PageControl).

Dabei ist es wichtig, dass ein Tastatur-Begeisteter User (zB ich) mittels Tab von einem Control zum nächsten springen kann. In der Verganhenheit konnte ich zwar ein Fenster einbinden, aber die TAB Taste hat sich dann immer nur im lokalen Fenster bewegt und nicht auf dem ganzen Fenster Breich (zB TabControl etc).

Dennoch: Schöner Nebenerffekt aktuell ist zB, die CMD in ein Fenster von meiner Anwendung einbinden zu können.
Ich hoffe das war verständlich - und auch dass der Zweck nun klarer ist :-)

Danke!

---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

Achja: Hab das mit dem Fenster schließen und SC_Close nun hinbekommen. Bitte nicht fragen wie, denn ich weiss es nicht. Auf einmal ging es.

Auch mit dem Themes bin ich ein wenig weiter gekommen.
Allerdings reagieren die Steuerelemnte immer noch falsch auf Mausereignisse etc. Hat mir da noch einer einen Tipp?
Bis jetzt habe ich: ControlStyle := [csNeedsBorderPaint, csParentBackground] hinzugefügt. Damit geht es schon halb :-)

Danke für die Hilfe!


Steve1024 - Mi 29.06.11 21:32

So... nun hab ich fast alles komplett.
Ich nun nur noch ein Problem: Wenn ich eine TForm einbinde, dann hab ich das Problem, dass ich jedes Control auswählen kann - auch TAB geht - aber, wenn ich direkt auf die Form klicke (nicht der Parent oder ein Child davon (zb. Edit); sondern die eigebunde Form bzw Controls die eigentlich nichts machen sollen (zB. Label)), dann spinnt alles.

Das ganze Fenster reagiert dann nicht mehr. Kein Minimieren, Maximieren, Schließen oder jegliche andere Eingabe. Wenn ich dann den Fokus auf eine andere Anwendung/anderes Fenster lege (durch klick) geht das Fenster wieder.

Hat von euch einer eine Idee warum das so ist?

Danke!