Einzelnen Beitrag anzeigen

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