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 WndProc bei Programmen im Hintergrund (https://www.delphipraxis.net/175627-wndproc-bei-programmen-im-hintergrund.html)

UliBru 4. Jul 2013 14:25

WndProc bei Programmen im Hintergrund
 
Hi,

ich hab da in einem Programm folgenden Code, um z.B. Play und Stop per Fernbedienung zu realisieren (Stichwort HID USB):
Delphi-Quellcode:
type
  TMainForm = class(TForm)
    ...
  protected
    procedure WndProc(var Message: TMessage); override;
  public
    procedure StopMyPlayback;
    procedure StartMyPlayback;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.WndProc(var Message: TMessage);
const
  FAPPCOMMAND_MASK = $F000;
var
  cmd: integer;
begin
  inherited;
  if (Message.Msg = WM_APPCOMMAND) then
  begin
    cmd := Message.LParamHi and not FAPPCOMMAND_MASK;
    case cmd of
      APPCOMMAND_MEDIA_STOP: StopMyPlayback;
      APPCOMMAND_MEDIA_PLAY: StartMyPlayback;
    end;
  end;
end;
Das läuft soweit. Aber nur, solange das Programm in Vordergrund aktiv ist.
Was ist zu tun, damit es auch startet und stoppt, wenn das Prog minimiert ist?
Wo finde ich vielleicht ein Beispiel?

Ich ahne bereits, dass da irgendein gewaltiger zusätzlicher Overhead nötig wird (Hook, dll etc.), aber vielleicht geht es doch ganz einfach.

Uli

sx2008 4. Jul 2013 16:30

AW: WndProc bei Programmen im Hintergrund
 
Du könntest dich in die Messageloop der Applikation einklinken:
Delphi-Quellcode:
procedure TMainForm.HandleOnMessage(var Msg: TMsg; var Handled: Boolean);
begin
  // Vorsicht! hier rauschen ALLE Messages der Anwendung durch
  // Debuggen ist schwierig weil jede Bewegung des Mauszeigers neu Messages auslöst
 
  if Msg.Message = WM_APPCOMMAND then
  begin
...
  end;
end;

...
procedure TMainForm.FormCreate(Sender:TObject);
begin
  Application.OnMessage := self.HandleOnMessage;

UliBru 4. Jul 2013 18:36

AW: WndProc bei Programmen im Hintergrund
 
Ich hab das mit dem HandleOnMessage probiert, es klappt aber nicht.
Wenn ich anstelle von
Delphi-Quellcode:
  if Msg.Message = WM_APPCOMMAND then
testweise einmal
Delphi-Quellcode:
  if Msg.Message = WM_KEYDOWN then
verwende und mir dazu eine Testausgabe mache, dann klappt es.
D.h. dass also das WM_APPCOMMAND im HandleOnMessage anscheinend nicht ankommt.

Uli

UliBru 4. Jul 2013 18:49

AW: WndProc bei Programmen im Hintergrund
 
Eine weitere Feststellung:
Beim Tastendruck auf die Fernbedienung wird auch das Ereignis WM_KEYDOWN ausgelöst. Nun gut, evtl. wäre da mit einer entsprechenden Auswertung auch irgendwas darstellbar.

ABER: wenn das Programm im Hintergrund läuft, wird auch kein WM_KEYDOWN mehr verarbeitet.
Insofern hilft das Application.OnMessage auch nicht weiter. Es bekommt keine Tastatur-Message sobald das Programm nicht aktiv im Vordergrund steht.

Uli

UliBru 5. Jul 2013 11:09

AW: WndProc bei Programmen im Hintergrund
 
Während es keine weitere Antwort mehr gegeben hat habe ich mich durch gefühlte 1001 Links zum Thema Global Hooks etc. gewühlt.

Eine Lösung habe ich bekommen mit RegisterHotkey. Dabei fängt aber dann mein Programm eben die Taste der Fernbedienung ab und andere Programme, z.B. Mediaplayer, bekommen dann die Funktion nicht mehr mitgeteilt.
Ein globales Hooken mit eigener zusätzlicher dll ist ebenfalls reichlich aufwendig.

Die Lösung, die nun genau das tut, was es soll, sieht so aus (dabei werden zwei externe Windows API-Funktionen eingebunden):
Delphi-Quellcode:
type
  TMainForm = class(TForm)
    ...
  private
    ShellHookMessage: Integer;
  protected
    procedure WndProc(var Message: TMessage); override;
  public
    procedure StopMyPlayback;
    procedure StartMyPlayback;
  end;

var
  MainForm: TMainForm;

function RegisterShellHookWindow(hWnd: hWnd): boolean; stdcall; external 'User32.dll' name 'RegisterShellHookWindow';
function DeregisterShellHookWindow(hWnd: hWnd): boolean; stdcall; external 'User32.dll' name 'DeregisterShellHookWindow';

implementation

{$R *.dfm}

procedure TMainForm.WndProc(var Message: TMessage);
const
  FAPPCOMMAND_MASK = $F000;
var
  cmd: integer;
begin
  inherited;
  if (Message.Msg = ShellHookMessage) and (Message.WParam = HSHELL_APPCOMMAND) then
  begin
    cmd := Message.LParamHi and not FAPPCOMMAND_MASK;
    case cmd of
      APPCOMMAND_MEDIA_STOP: StopMyPlayback;
      APPCOMMAND_MEDIA_PLAY: StartMyPlayback;
    end;
  end;
end;

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  DeregisterShellHookWindow(Handle);
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  ShellHookMessage := RegisterWindowMessageA('MYSHELLHOOK');
  RegisterShellHookWindow(Handle);
end;
Damit kommen die Kommandos der Fernbedienung an, egal ob das Programm im Vorergrund ist oder nicht. Und die Kommandos werden auch an andere Programme weitergereicht (so sie es denn sollen).

Vielleicht hilft der Code ja auch mal anderen.:-D

Uli

Luckie 8. Jul 2013 02:22

AW: WndProc bei Programmen im Hintergrund
 
Delphi-Quellcode:
RegisterWindowMessageA('MYSHELLHOOK');
'MYSHELLHOOK' ist auch nicht so toll. Da gibt es bestimmt einige. Nimm eine GUID und du bist auf der sicheren Seite.

Ich würde noch prüfen, ob RegisterShellHookWindow nicht fehl geschlagen ist.

UliBru 8. Jul 2013 07:24

AW: WndProc bei Programmen im Hintergrund
 
Zitat:

Zitat von Luckie (Beitrag 1221020)
Nimm eine GUID und du bist auf der sicheren Seite.

Ich würde noch prüfen, ob RegisterShellHookWindow nicht fehl geschlagen ist.

Danke. Mit GUID bin ich einverstanden.

Müsste denn ein optimierter Code nicht in etwa so aussehen?
Delphi-Quellcode:
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if shellHookWindowRegisteredSuccessfully then
    DeregisterShellHookWindow(Handle);
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  ShellHookMessage := RegisterWindowMessageA(myGUID);
  if ShellHookMessage <> 0 then
    shellHookWindowRegisteredSuccessfully := RegisterShellHookWindow(Handle);
  if not shellHookWindowRegisteredSuccessfully then
    MachIrgendeineFehlerbehandlung;
end;
Ansonsten, wenn RegisterShellHookWindow fehlschlägt, dann findet ja schlichtweg das WndProc mit
Delphi-Quellcode:
Message.Msg = ShellHookMessage
nicht statt. MachIrgendeineFehlerbehandlung dient dann nur dazu, den Anwender gezielt zu informieren.

Eine generelle Frage, vermutlich einen eigenen Thread wert: man findet x Beispiele, welche die Funktionsergebnisse von API-Aufrufen nicht auswerten. Was ist besser: trotzdem auswerten und das Programm mit Fehlerauswertungen aufblähen oder doch auch ab und zu das Ignorieren von Funktionsergebnissen zulassen? Klar ist, dass das erstere das sicherere Verfahren ist.

DeddyH 8. Jul 2013 07:54

AW: WndProc bei Programmen im Hintergrund
 
Zitat:

Zitat von UliBru (Beitrag 1221026)
Was ist besser: trotzdem auswerten und das Programm mit Fehlerauswertungen aufblähen oder doch auch ab und zu das Ignorieren von Funktionsergebnissen zulassen?

Es geht ja nicht in erster Linie um Fehlermeldungen, sondern darum, dass man ggf. mit einer Funktionsrückgabe (z.B. einem Handle) weiterarbeitet. Da wäre es doch sinnvoll, erst zu prüfen, ob diese Daten auch gültig sind. Was man im Fehlerfall tut, hängt ja vom Einzelfall ab, evtl. kann man das auch kaltlächelnd ignorieren.

UliBru 8. Jul 2013 08:06

AW: WndProc bei Programmen im Hintergrund
 
:-D

Das Handle in
Delphi-Quellcode:
RegisterShellHookWindow(Handle)
ist kein Rückgabewert sondern ein Eingabewert, hier also das Handle des Programms (ich glaube Self.Handle wäre in diesem Fall dasselbe). Der Rückgabewert der API-Funktion ist boolean und zeigt lediglich an, dass die Registrierung erfolgreich war.
Siehe auch http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Der eigentlich verwendete Rückgabewert ist im gegebenen Fall die Variable ShellHookMessage, die in WndProc verwendet wird.

DeddyH 8. Jul 2013 08:13

AW: WndProc bei Programmen im Hintergrund
 
Erst kündigst Du eine generelle Frage an, und dann beziehst Du Dich doch auf das spezifische Problem?


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:15 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