AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi DLL-Fenster erhält nicht alle Keyboard-Events?!
Thema durchsuchen
Ansicht
Themen-Optionen

DLL-Fenster erhält nicht alle Keyboard-Events?!

Ein Thema von chrschn · begonnen am 17. Nov 2004 · letzter Beitrag vom 19. Nov 2004
Antwort Antwort
chrschn
(Gast)

n/a Beiträge
 
#1

DLL-Fenster erhält nicht alle Keyboard-Events?!

  Alt 17. Nov 2004, 00:18
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:
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;
Die DLLs werden dann in einem TTreeView im Hauptfenster hierarchisch dargestellt. Im Hauptfenster rufe ich die Funktionen dann wie folgt auf:

Delphi-Quellcode:
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;
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.

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:
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;
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.

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
  Mit Zitat antworten Zitat
chrschn
(Gast)

n/a Beiträge
 
#2

DLL-Fenster erhält nicht alle Keyboard-Events?! GEHT JETZT!

  Alt 18. Nov 2004, 18:20
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:
  • Sämtliche Zeichen können erzeugt werden, auch Akzente (Dead-Chars), [ALT GR]-Zeichen usw.
  • Pfeiltasten, Home, End, PgUp, PgDn usw. können erzeugt werden
  • Die Hostanwendung funktioniert nach meiner Beobachtung genauso wie vorher ([Tab]: Focuswechsel, Return: Default-Button, [ALT]+MenuAccel: Tastenkürzel, ...)
Was allerdings (noch) nicht funktioniert:
  • [TAB] innerhalb des DLL-Fensters wechselt nicht den Focus innerhalb desselben, sondern springt zum nächsten Control der Hostanwendung
  • Der Focuswechsel durch [TAB] in der Hostanwendung überspringt das DLL-Fenster
  • MenuAccels im DLL-Fenster funktionieren nicht, also z. B. Button-Captions wie '&OK' können nicht mit [ALT]+[O] aktiviert werden
Mit diesen Einschränkunten kann ich allerdings erstmal leben .

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:
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;
Gruß,
Christian
  Mit Zitat antworten Zitat
chrschn
(Gast)

n/a Beiträge
 
#3

Re: DLL-Fenster erhält nicht alle Keyboard-Events?! GEHT JET

  Alt 19. Nov 2004, 00:21
Zitat von chrschn:
Was allerdings (noch) nicht funktioniert:
  • [TAB] innerhalb des DLL-Fensters wechselt nicht den Focus innerhalb desselben, sondern springt zum nächsten Control der Hostanwendung
  • Der Focuswechsel durch [TAB] in der Hostanwendung überspringt das DLL-Fenster
  • MenuAccels im DLL-Fenster funktionieren nicht, also z. B. Button-Captions wie '&OK' können nicht mit [ALT]+[O] aktiviert werden
Eher zufällig bin ich noch auf eine Lösung für das letzte der genannten Probleme gestoßen, nämlich die Menu-Accelerators. Ich habe meine Prozedur zum anzeigen des DLL-Fensters um einen var-Parameter erweitert. Dieser liefert der Host-Anwendung das Handle des DLL-Fensters zurück. In der DLL sieht das so aus:
Delphi-Quellcode:
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;
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:
Delphi-Quellcode:
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;
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.

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
  Mit Zitat antworten Zitat
chrschn
(Gast)

n/a Beiträge
 
#4

Re: DLL-Fenster erhält nicht alle Keyboard-Events?! GEHT JET

  Alt 19. Nov 2004, 00:50
Zitat von chrschn:
Zitat von chrschn:
Was allerdings (noch) nicht funktioniert:
  • [TAB] innerhalb des DLL-Fensters wechselt nicht den Focus innerhalb desselben, sondern springt zum nächsten Control der Hostanwendung
  • Der Focuswechsel durch [TAB] in der Hostanwendung überspringt das DLL-Fenster
  • MenuAccels im DLL-Fenster funktionieren nicht, also z. B. Button-Captions wie '&OK' können nicht mit [ALT]+[O] aktiviert werden
Eher zufällig bin ich noch auf eine Lösung für das letzte der genannten Probleme gestoßen, nämlich die Menu-Accelerators. Ich habe meine Prozedur zum anzeigen des DLL-Fensters um einen var-Parameter erweitert. Dieser liefert der Host-Anwendung das Handle des DLL-Fensters zurück.
Da ich jetzt schonmal das Handle des DLL-Fensters hatte, lag es nahe, auch gleich den Focus-Wechsel wie oben beschrieben mit einzubauen. Dazu habe ich das Panel, in dem die DLL-Fenster jeweils angezeigt werden, fokusierbar gemacht (Panel.TabStop := True). Dann habe ich für das Panel folgenden OnEnter-Event-Handler geschrieben:
Delphi-Quellcode:
var
  DllDlgHandle: THandle

[...]

procedure TConfigForm.ConfigPanelEnter(Sender: TObject);
begin
  if (DllDlgHandle > 0) then
     Windows.SetFocus(DllDlgHandle);
end;
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

Schönen Gruß,

Christian
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:50 Uhr.
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