AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Windows-Messages sinnvoll verteilen

Ein Thema von TiGü · begonnen am 21. Okt 2014 · letzter Beitrag vom 22. Okt 2014
Antwort Antwort
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.060 Beiträge
 
Delphi 10.4 Sydney
 
#1

Windows-Messages sinnvoll verteilen

  Alt 21. Okt 2014, 17:01
Hallo Gemeinde,

in einen alten, von mir betreuten Programm wird sehr viel mit PostMessage gearbeitet, um von Unterformularen oder Threads irgendetwas an das Hauptformular zu schicken/signalisieren.

Da gibt es an ein paar Stellen im Quelltext Notifizierungslisten (normales TList) oder es werden TWinControl-Objekte oder deren Handles per langer Constructor-Kette an den richtigen Ort gereicht, um die PostMessage mit einen Windowhandle zu versorgen.
An einer Stelle gibt es auch eine böse globale Variable, wo die Instanz des Hauptformulars gespeichert wird.

Alles in allen sehr unschön, durcheinander und irgendwie unnötig.
Viele Programmteile bzw. Units müssen sich kennen, obwohl sie es eigentlich nicht müssten.

Ich spiele seit einiger Zeit mit dem Gedanken den folgenden globalen Verteil-Mechanismus zu implementieren und würde mich über Kritik und Anregungen freuen!
Delphi-Quellcode:
interface

uses
  Messages,
  System.Generics.Collections,
  Winapi.Windows,
  Vcl.Controls,
  System.SyncObjs;

type
  IMessageDistributor = interface
    procedure Subscribe(const Msg : UINT; const Listener : TWinControl); overload;
    procedure Subscribe(const Msgs : array of UINT; const Listener : TWinControl); overload;
    procedure Unsubscribe(const Msg : UINT; const Listener : TWinControl); overload;
    procedure Unsubscribe(const Msgs : array of UINT; const Listener : TWinControl); overload;
    function PostMessageToSubscribers(const Msg : UINT; const WParameter : WPARAM = 0; const LParameter : LPARAM = 0) : Boolean;
  end;

  TMessageDistributor = class(TInterfacedObject, IMessageDistributor)
  strict private
  type
    TListenerList = TList<TWinControl>;
  strict private
    FMessageToListenerMapping : TDictionary<UINT, TListenerList>;
    FCritcalSection : TCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Subscribe(const Msg : UINT; const Listener : TWinControl); overload;
    procedure Subscribe(const Msgs : array of UINT; const Listener : TWinControl); overload;
    procedure Unsubscribe(const Msg : UINT; const Listener : TWinControl); overload;
    procedure Unsubscribe(const Msgs : array of UINT; const Listener : TWinControl); overload;

    function PostMessageToSubscribers(const Msg : UINT; const WParameter : WPARAM = 0; const LParameter : LPARAM = 0) : Boolean;
  end;

function MessageDistributor : IMessageDistributor;

implementation

var
  _MessageDistributor : IMessageDistributor;

function MessageDistributor : IMessageDistributor;
begin
  if not Assigned(_MessageDistributor) then
  begin
    _MessageDistributor := TMessageDistributor.Create;
  end;
  Result := _MessageDistributor;
end;

{ TMessageDistributor }

constructor TMessageDistributor.Create;
begin
  FCritcalSection := TCriticalSection.Create;
  FMessageToListenerMapping := TObjectDictionary<UINT, TListenerList>.Create([doOwnsValues]);
end;

destructor TMessageDistributor.Destroy;
begin
  FMessageToListenerMapping.Free;
  FCritcalSection.Free;
  inherited;
end;

function TMessageDistributor.PostMessageToSubscribers(const Msg : UINT; const WParameter : WPARAM = 0; const LParameter : LPARAM = 0) : Boolean;
var
  Listeners : TListenerList;
  Listener : TWinControl;
begin
  Result := False;
  FCritcalSection.Enter;
  try
    if FMessageToListenerMapping.TryGetValue(Msg, Listeners) then
    begin
      for Listener in Listeners do
      begin
        if Assigned(Listener) then
        begin
          Result := PostMessage(Listener.Handle, Msg, WParameter, LParameter);
        end;
      end;
    end;
  finally
    FCritcalSection.Leave;
  end;
end;

procedure TMessageDistributor.Subscribe(const Msgs : array of UINT; const Listener : TWinControl);
var
  I : Integer;
begin
  for I := Low(Msgs) to High(Msgs) do
  begin
    Subscribe(Msgs[I], Listener);
  end;
end;

procedure TMessageDistributor.Unsubscribe(const Msgs : array of UINT; const Listener : TWinControl);
var
  I : Integer;
begin
  for I := Low(Msgs) to High(Msgs) do
  begin
    Unsubscribe(Msgs[I], Listener);
  end;
end;

procedure TMessageDistributor.Subscribe(const Msg : UINT; const Listener : TWinControl);
var
  Listeners : TListenerList;
begin
  if FMessageToListenerMapping.TryGetValue(Msg, Listeners) then
  begin
    if Assigned(Listeners) then
    begin
      if not Listeners.Contains(Listener) then
        Listeners.Add(Listener);
    end;
  end
  else
  begin
    Listeners := TListenerList.Create;
    Listeners.Add(Listener);
    FMessageToListenerMapping.Add(Msg, Listeners);
  end;
end;

procedure TMessageDistributor.Unsubscribe(const Msg : UINT; const Listener : TWinControl);
var
  Listeners : TListenerList;
begin
  if FMessageToListenerMapping.TryGetValue(Msg, Listeners) then
  begin
    if Assigned(Listeners) then
    begin
      Listeners.Remove(Listener);
    end;
  end;
end;
Hier noch kleines Beispielprojekt (mit XE3 erstellt), was sinnbefreite Beispiele der Anwendung zeigt.
Angehängte Dateien
Dateityp: zip PostMessageTest.zip (7,9 KB, 6x aufgerufen)

Geändert von TiGü (21. Okt 2014 um 17:12 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.009 Beiträge
 
Delphi 12 Athens
 
#2

AW: Windows-Messages sinnvoll verteilen

  Alt 21. Okt 2014, 17:08
Weiß nicht ob es passt, aber vielleicht gibt es ein paar Anregungen: Event-Driven, Asynchronous Development with Delphi and the LKSL
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#3

AW: Windows-Messages sinnvoll verteilen

  Alt 21. Okt 2014, 17:57
Warum willst du dich überhaupt auf Windows-Messages und auch die Verwendung von PostMessage festlegen?

Nimm doch einfach TThread.Queue und schon geht das auch auf jeder Plattform.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.060 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: Windows-Messages sinnvoll verteilen

  Alt 22. Okt 2014, 09:28
Warum willst du dich überhaupt auf Windows-Messages und auch die Verwendung von PostMessage festlegen?
Nun ja, es ist eine über 10 Jahre alte Windows-Desktop-VCL-Anwendung, die wohl bis die Hölle zufriert nicht das Licht der Apfelsonne sehen wird oder gegen Androiden bestehen muss.
Von daher ist die "Einschränkung" auf Windows-Messages in dem Sinne keine.

Nimm doch einfach TThread.Queue und schon geht das auch auf jeder Plattform.
Verstehe den Ansatz mit Queue so, dass dadurch von gerade laufenden Threads aus entsprechende Methoden des Threads im Formularkontext/VCL-Main-Thread ausgeführt werden, richtig?
Häufig senden sich in meinem Anwendungsfall aber auch Unterformulare untereinander oder zum Hauptformular Benachrichtigungen.
Wie würde man das mit TThread.Queue lösen?

Des Weiteren besteht ja schon die Botschaftsbehandlung durch entsprechende Message-Handler in den Formularen, so dass ich "nur" relativ schmerzlos das anmelden und senden der Botschaften in diesem Ansatz anpassen müsste.

Ich wollte mit dem Posten des Quelltextes nur sicher gehen, dass ich nicht irgendetwas Elementares übersehen habe.
Unter der Prämisse, dass es halt nur auf Windows läuft, was hältst du denn von diesem Lösungsansatz zum Entkoppeln der einzelnen Units untereinander?
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.060 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Windows-Messages sinnvoll verteilen

  Alt 22. Okt 2014, 09:28
Weiß nicht ob es passt, aber vielleicht gibt es ein paar Anregungen: Event-Driven, Asynchronous Development with Delphi and the LKSL
Vielen Dank, beim ersten Überfliegen sieht das nach interessanter Lektüre aus.
Habe mir ein Lesezeichen gesetzt und werde das am Wochenende mal ausführlicher studieren.
  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 18:28 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