Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Hook für eigene Windows Message (https://www.delphipraxis.net/152628-hook-fuer-eigene-windows-message.html)

s.h.a.r.k 29. Jun 2010 23:34

Hook für eigene Windows Message
 
Hallo zusammen,

und zwar streite ich mich immer noch mit meinen Threads. Ich habe im Moment einen LogController, der von verschiedenen Threads aus bedient werden kann, d.h. die Log()-Methode steht für diese zur Verfügung. In der Log-Methode wird eine Queue gefüllt und via PostThreadMessage eine Nachricht an den Haupt-Thread geschickt.

Beim Erzeugen des LogControllers (was im HauptThread geschieht -> explizites synchronisiertes Erzeugen) wird ein globaler Hook registriert:
Delphi-Quellcode:
const
  WM_LOG_NOTIFIER = WM_USER + 123;

////////////////////////////////////////////////////////////////////////////////////////////////////
////
//
//
constructor TLogController.Create();
var
  Method: TMethod;
begin
  inherited Create();

  { ... }

  // FHookMethodPointer and FHookHandle are private variables
  Method.Code := @TLogController.HookProc;
  Method.Data := Self;
  FHookMethodPointer := MakeProcInstance(Method);
  FHookHandle := SetWindowsHookEx(WH_GETMESSAGE, FHookMethodPointer, 0, System.MainThreadID);
end;

////////////////////////////////////////////////////////////////////////////////////////////////////
////
//
//
function TLogController.HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LResult; stdcall;
var
  P: PMsg;
begin
  case nCode < HC_ACTION of
    True:
      Result := CallNextHookEx(FHookHandle, nCode, wParam, lParam);
  else
    if (lParam <> 0) then
    begin
      P := PMsg(lParam);
      if (P^.message = WM_LOG_NOTIFIER) then
        Self.DoPropagateLog();
    end;

    Result := CallNextHookEx(FHookHandle, nCode, wParam, lParam);
  end;
end;
Nun ist es so, dass dieser Hook nicht wirklich auf die Nachricht reagiert, die via PostThreadMessage() versendet wird. Hier noch der Aufruf:
Delphi-Quellcode:
PostThreadMessage(System.MainThreadID, WM_LOG_NOTIFIER, 0, 0);


Ich sehe nicht wirklich, warum der Hook nicht auf diese Nachricht reagieren sollte :gruebel: Wobei ich sagen muss, dass ich nicht weiß, ob PMsg der richtige Typ für lParam ist. Ich habe bisher noch nichts passenderes gefunden.

s.h.a.r.k 30. Jun 2010 03:43

AW: Hook für eigene Windows Message
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hab mir nun ein externes kleines Programm geschrieben, bei dem der Hook funktioniert. In der SetWindowsHookEx muss WH_GETMESSAGE verwendet werden und in der Hook-Function ist lParam von Typ PMsg. Nur erhalte ich nun diese eine Nachricht doppelt *grml* Aber das ist eine neue Frage, ergo ein neuer Thread.

Anbei noch das Test-Project, falls es mal jemandem helfen sollte.

// edit

So, anbei nun die gefixte Version, in der nur einmal die Message beachtet wird. Dann will ich mal hoffen, dass es in meinem aktuellen Projekt korrekt funktioniert ;)

SirThornberry 30. Jun 2010 09:15

AW: Hook für eigene Windows Message
 
Ich tippe hier ganz stark auf einen Designfehler. Warum willst du deine eigene Message hooken? wäre es nicht sinnvoller die Nachricht nicht direkt zu schicken sondern über eine Klasse die du dafür erstellst? Und diese Klasse kann das loggen ja dann übernehmen (bereits beim absenden).

s.h.a.r.k 30. Jun 2010 12:42

AW: Hook für eigene Windows Message
 
Hm, ich lasse mich gerne vom Gegenteil überzeugen und mir ein einfacheres Konzept unterbreiten :mrgreen: Hier das Problem, welche ich erschlagen muss:

Ich habe einen LogController, der als Singleton-Pattern angelegt ist, d.h. ich hole mit die Instanz via einer statischen Klassenmethode
Delphi-Quellcode:
TLogController.GetInstance()
. Ich logge ebenso über eine statische Klassenmethode
Delphi-Quellcode:
TLogController.Log( {Parameter} );
, die intern die GetInstance()-Methode verwendet.

Es gibt die Möglichkeit bei dem LogController EventListener zu registrieren, d.h. ich übergebe einen Zeiger auf eine Methode, die dann gecalled wird. Ebenso kann ich auch Module registrieren, die mehr Funktionalitäten bieten -- diese Module implementieren meinem Interface ILogModule. Ein Module ist im Moment z.B. das FileLogModule, welches, wie der Name schon sagt, die eintreffenden Logs in bestimmte Dateien schreibt.

Ganz wichtig ist, dass dieser LogController von mehreren Threads parallel benutzt werden kann und somit threadsicher sein muss. Ich habe in letzter Zeit schon mehrere Threads (hier im Forum ;) ) geschrieben, die mir Lösungen zu diesem Problem geben sollten. So recht gefällt mir das mit diesem globalen Thread ja auch nicht. Jedenfalls habe ich im Moment nun folgendes Konzept: Die statische Klassenmethode Log() ist mit CriticalSections geschützt. Intern werden verschiedene Queues gefüllt -- für alle EventListener eine und für jedes Module separat eine. Danach wird die Nachricht WM_LOG_NOTIFIER (selbst definiert) via PostThreadMessage an den MainThread geschickt. Nun will ich ja, dass ich für den LogController keinerlei weiteren Code in das eigentliche Programm schreiben muss, da dies sonst Probleme beim Ändern oder Entfernen des LogControllers geben kann. Daher will ich in den MainThread auch nicht die passende Weiterleitung der Nachricht implementieren und mir bleibt nur der Weg über den globalen Hook.

Warum aber diese Nachricht? Das hat den Hintergrund, dass die EventListener und Module synchronisert benachrichtigt werden sollen (ohne Messages, da ich das bisherige Konzept beibehalten will), da teilweise auf die VCL zugegriffen werden kann. Die Message an den MainThread sorgt dafür, dass der Code für die Benachrichtigung auf unter dessen Kontext statt findet. Nachdem der MainThread ja unter Umständen auch mal blockiert sein kann (durch die CriticalSection in der Log()-Methode) ist eine Nachricht eben das/ein passende Mittel, um einen Deadlock zu umgehen -- weitere kenne ich im Moment leider nicht.

Ich habe im Moment aber auch noch die folgende Idee: Die Log-Methode wird komplett umgebaut, sodass darin in etwa folgendes steht (frei runtergeschrieben):
Delphi-Quellcode:
class procedure TLogController.Log(const AMessage: String; const ALogType: TLogType);
begin
  TThread.Synchronize(
    nil
    procedure()
    var
      LR : PLogRecord;
    begin
      New(LR);
      LR^.Text := AMessage;
      LR^.MsgType := ALogType;
      PostMessage(TLogController.Handle, WM_LOG_NOTIFIER, Integer(LR), 0);
    end
  );
end;
Die Idee hierbei ist es, dass ich dem TLogController ein Handle zur hand gebe und die Benachrichigung syncronisiert da hin schicke. Ich weiß nur leider nicht, ob das möglich ist, da ic das nur konzeptionell in meinem Kopf habe :mrgreen:

Wenn jemand aber die super Idee hat, dann teile er sie mir doch bite mit :-D Herzlichsten Dank!

s.h.a.r.k 30. Jun 2010 19:50

AW: Hook für eigene Windows Message
 
Also, noch als kurzes Feedback zu meinem letzen Beitrag: ich habe nun auf den globalen Hook verzichtet, da es nun wahrlich mit Kanonen auf Bits geschossen ist. Ich habe die Idee umgesetzt, die ich weiter unten erwähnt habe: synchronisiertes PostMessage. Es funktioniert einwandfrei :mrgreen: (endlich mal)


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:17 Uhr.

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