|
Antwort |
chrschn
(Gast)
n/a Beiträge |
#1
Hi all.
Ich habe folgendes Problem: Ich habe eine Anwendung geschrieben, die sozusagen Plugin-DLLs verwendet. Diese DLLs haben auch einen Konfigurationsform. Da ich aber mit meiner Anwendung nicht die Borland-MM-DLL weitergeben möchte, verwende ich nur einfache Datentypen, Pointer, PChar usw. Damit das Konfigurationsform im Haupt-Konfigurationsform der EXE angezeigt wird, zeige ich das jeweilige DLL-Fenster in einem TPanel auf meinem Hauptfenster an. Die DLLs exportieren alle in etwa folgende Funktion:
Delphi-Quellcode:
Die DLLs werden dann in einem TTreeView im Hauptfenster hierarchisch dargestellt. Im Hauptfenster rufe ich die Funktionen dann wie folgt auf:
procedure ShowConfigDlg(const ID: Integer; const ParentWindow: THandle;
const Left: Integer; const Top: Integer; const Width: Integer; const Height: Integer); begin if not Assigned(ConfigForm) then begin ConfigForm := TConfigForm.Create(NIL); ConfigForm.ParentWindow := ParentWindow; end; ConfigForm.Left := Left; ConfigForm.Top := Top; ConfigForm.Width := Width; ConfigForm.Height := Height; ConfigForm.Visible := true; end;
Delphi-Quellcode:
Das klappt auch alles ganz gut. Allerdings erhalten die Komponenten wie TEdit in dem DLL-Fenster nicht alle Tastatur-Ereignisse. Genauer: Ich habe den Eindruck, sie erhalten die Ereignisse, aber so, als ob die ALT-Taste dabei gedrückt würde. Ich kann in die Edit-Felder Zahlen und die meisten Buchstaben eingeben (nicht alle!), allerdings kann ich mich nicht mit den Pfeiltasten links oder rechts im Text bewegen.
type
TShowConfigDlgProc = procedure(const ID: Integer; const ParentWindow: THandle; const Left: Integer; const Top: Integer; const Width: Integer; const Height: Integer); var ShowCfgDlg: TShowConfigDlgProc; Handle: THandle; begin Handle := LoadLibrary(PChar(FileName)); @ShowCfgDlg := GetProcAddress(fHandle, 'ShowConfigDlg'); ShowConfigDlg(ConfigPanel.Handle, 0, 0, ConfigPanel.ClientWidth, ConfigPanel.ClientHeight) end; Wenn ich 'a' tippe, passiert gar nix, wenn ich 'c' tippe, wird das Haupt-Konfigurationsfenster der EXE geschlossen, als ob ich ALT+C gedrückt hätte. Wenn ich für die besagten Edit-Felder im DLL-Fenster eine OnKeyDown oder OnKeyPress-Routine schreibe, dann wird tatsächlich nur für die Tasten ein Event ausgelöst, die ich tippen kann, also z. B. nicht für 'a'. Ich habe es dann mal mit einem TApplicationEvents versucht, den ich auf dem Haupt-Konfigurationsfenster platziert habe. Für diesen habe ich dann folgenden OnMessage-Handler geschrieben:
Delphi-Quellcode:
Damit kann ich jetzt immerhin die Pfeiltasten in den DLL-Edits verwenden. Die "normalen" Buchstaben bekomme ich trotzdem nicht alle. Wenn ich die Zeile "if (Msg.wParam in SYS_KEYS)" auskommentiere, habe ich überhaupt keine Buchstaben mehr im DLL-Fenster, nur noch Pfeiltasten.
procedure TConfigForm.ApplicationKeyboardEventsMessage(var Msg: tagMSG;
var Handled: Boolean); const SYS_KEYS: set of Byte = [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_TAB, VK_RETURN]; var Handle: THandle; begin if (Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP) then if (Msg.wParam in SYS_KEYS) then begin Handle := GetFocus; SendMessage(Handle, Msg.message, Msg.wParam, Msg.lParam); Handled := True; end; end; Ich glaube, ich könnte das ganze wohl über globale Tastatur-Hooks lösen, ich habe auch Assabard's Tutorial gelesen. Aber ich glaube, dass das für meinen Zweck absoluter Overkill ist, denn 1. muss dann jede DLL (und es sind ein paar) diese Hooks setzen, 2. muss jede die Ereignisse dann auch noch verarbeiten und 3. muss das doch auch irgendwie einfacher gehen Hat jemand einen schlauen Rat? Danke im Voraus! Christian |
Zitat |
chrschn
(Gast)
n/a Beiträge |
#2
Ich habe es jetzt nach einigen Experimenten hinbekommen. Die Lösung baut auf dem schon geposteten Versuch auf, die Keyboard-Messages über ein TApplicationEvents-Objekt abzufangen und an das Control mit dem Focus weiterzuleiten, also sowohl an die eigenen als auch an die DLL.
Allerdings müssen dazu nicht nur die WM_KEYDOWN und WM_KEYUP-Messages weitergeleitet werden. Darüber hinaus müssen die WM_KEYDOWN-Messages noch mit TranslateMessage in eine WM_CHAR oder WM_DEADCHAR-Message umgewandelt werden, welche dann auch noch weitergeleitet werden müssen. Außerdem dürfen die Tabulator oder Enter-Taste nicht weitergeleitet werden, sonst verlieren sie ihre spezielle Funktion. Keine Idee, woran das liegen könnte. Was jetzt funktioniert:
Um die Tabulator-Geschichte hinzubiegen, kam mir schon folgende Idee: Man könnte das Panel in den Focuswechsel mit einbeziehen (TPanel.TabStop := True) und sich das Handle des aktuell angezeigten DLL-Fensters von selbiger erfragen. Dann kann man im TPanel.OnEnter-Event-Handler das Fenster per Windows-API aktivieren, mit SetFocus(HandleOfCurrentDLL). Aber für weitere Experimente habe ich immo leider keine Zeit. Falls es jemand ausprobiert, bitte (Miss-)Erfolgsbereichte hier posten 8) Hier nun der Code für das TApplicationEvents.OnMessage in der Hostanwendung:
Delphi-Quellcode:
Gruß,
procedure TConfigForm.ApplicationKeyboardEventsMessage(var Msg: tagMSG;
var Handled: Boolean); const DONT_FORWARD_KEYS: set of Byte = [VK_RETURN, VK_TAB]; var Handle: THandle; VKey: Byte; begin // This event handler forwards the keyboard events to the DLL window. // Without it some keys would not be sent to the DLL window. This seems // to be an issue of either Delphi or the Windows message handling. case Msg.message of WM_KEYDOWN: begin // Translate the virutal key into a character key (0..255) VKey := Lo(MapVirtualKey(Msg.wParam, 2)); // If no translation has to be made, MapVirtualKey returns 0. if (VKey = 0) then VKey := Lo(Msg.wParam); // Don't handle the DONT_FORWARD_KEYS keys as they have special // functions and must be handled by the application itself. if not (VKey in DONT_FORWARD_KEYS) then begin // Get current focused control Handle := GetFocus; // Forward the message to it SendMessage(Handle, Msg.message, Msg.wParam, Msg.lParam); // Process message by translating the WM_KEYUP message into a // WM_CHAR message. Handled := TranslateMessage(Msg); end; end; WM_KEYUP: begin // Translate the virutal key into a character key (0..255) VKey := Lo(MapVirtualKey(Msg.wParam, 2)); // If no translation has to be made, MapVirtualKey returns 0. if (VKey = 0) then VKey := Lo(Msg.wParam); // Don't handle the DONT_FORWARD_KEYS keys as they have special // functions and must be handled by the application itself. if not (VKey in DONT_FORWARD_KEYS) then begin // Get current focused control Handle := GetFocus; // Forward the message to it SendMessage(Handle, Msg.message, Msg.wParam, Msg.lParam); Handled := True; end; end; WM_DEADCHAR, WM_CHAR: begin VKey := Msg.wParam; // Don't handle the DONT_FORWARD_KEYS keys as they have special // functions and must be handled by the application itself. if not (VKey in DONT_FORWARD_KEYS) then begin // Get current focused control Handle := GetFocus; // Forward the message to it SendMessage(Handle, Msg.message, Msg.wParam, Msg.lParam); Handled := True; end; end; end; end; Christian |
Zitat |
chrschn
(Gast)
n/a Beiträge |
#3
Zitat von chrschn:
Was allerdings (noch) nicht funktioniert:
Delphi-Quellcode:
Wenn das DLL-Fenster angezeigt wird, dann weise ich in der Host-Anwendung die Eigenschaft Application.DialogHandle zu (siehe Delphi-Hilfe von TApplication.DialogHandle). Der Code der Host-Anwendung:
procedure ShowConfigDlg(const ID: Integer; const ParentWindow: THandle;
const Left: Integer; const Top: Integer; const Width: Integer; const Height: Integer; var DlgHandle: THandle); begin if not Assigned(ConfigForm) then begin ConfigForm := TConfigForm.Create(NIL); ConfigForm.ParentWindow := ParentWindow; end; DlgHandle := ConfigForm.Handle; ConfigForm.Left := Left; ConfigForm.Top := Top; ConfigForm.Width := Width; ConfigForm.Height := Height; ConfigForm.Visible := true; end; exports ShowConfigDlg;
Delphi-Quellcode:
Und siehe da -- die MenuAccels funktionieren . Das TApplicationEvents-Objekt auf dem Form der Host-Anwendung ist aber immer noch nötig, wie schon zuvor beschrieben. Wenn das DLL-Fenster wieder ausgeblendet wird, muss Application.DialogHandle := 0 gesetzt werden.
type
TShowConfigDlgProc = procedure(const ID: Integer; const ParentWindow: THandle; const Left: Integer; const Top: Integer; const Width: Integer; const Height: Integer; var DlgHandle: THandle); var ShowCfgDlg: TShowConfigDlgProc; DlgHandle, Handle: THandle; begin Handle := LoadLibrary(PChar(FileName)); @ShowCfgDlg := GetProcAddress(fHandle, 'ShowConfigDlg'); ShowConfigDlg(ConfigPanel.Handle, 0, 0, ConfigPanel.ClientWidth, ConfigPanel.ClientHeight, DlgHandle); Application.DialogHandle := DlgHandle; end; Den Umweg über das hin- und her mit dem Handle muss man leider gehen. Wenn in der DLL die Unit "Forms" eingebunden wird und dort Application.DialogHandle gesetzt wird, funktioniert es scheinbar nicht. Ich vermute mal, weil die höhere Instanz (in dem Fall die Host-Anwendung) die Message-Queue für die DLL entsprechend anpassen muss, und nicht umgekehrt. Schönen Gruß, Christian |
Zitat |
chrschn
(Gast)
n/a Beiträge |
#4
Zitat von chrschn:
Zitat von chrschn:
Was allerdings (noch) nicht funktioniert:
Delphi-Quellcode:
Das aktiviert das DLL-Fenster und auch dessen Komponente, falls vorher schon eine ausgewählt war. Nun kann ich mit [TAB] in der Host-Anwendung auch in das DLL-Fenster springen. Allerdings springt das nächste [TAB] zum nächsten Control der Host-Anwendung, und nicht zum nächsten Control des DLL-Fensters. Naja, man kann wohl nicht alles haben
var
DllDlgHandle: THandle [...] procedure TConfigForm.ConfigPanelEnter(Sender: TObject); begin if (DllDlgHandle > 0) then Windows.SetFocus(DllDlgHandle); end; Schönen Gruß, Christian |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |