Entwickler-Ecke

Open Source Units - TMutexIPC


BenBE - Fr 13.08.04 22:25
Titel: TMutexIPC


.Chef - Do 19.08.04 21:14
Titel: Re: TMutexIPC
BenBE hat folgendes geschrieben:
Diese Komponente entstand, bis auf das MMF und Mutex-Beispiel von Chef (7. Beitrag des Threads), UNABHÄNGIG von seinem Source.

Der Vollständigkeit poste ich auch mal meine Variante, die die gleiche Funktion erfüllt, vom Prinzip her an manchen Stellen aber etwas abweicht. Es gelten die selben Lizenzbestimmungen wie beim BenBE.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
unit MutexMIC;

interface

uses
  Windows, Messages, SysUtils, Classes, Forms, Dialogs;

type
  TReceiveEvent = procedure(SourceID : Byte;var X;Size : Word) of object;
  TConnectEvent = procedure(SourceID : Byte) of object;
  TOnMessage = procedure(var msg: tagMsg; var Handled: Boolean) of object;

  TMutexMIC = class(TComponent)
  private
    MMFSize, MMFBlock : Cardinal;
    InstanceNumber : Byte;
    InstanceRunning : array[0..254of Boolean;
    MainMutex : THandle;
    HMMF : THandle;
    LockMutex : THandle;
    PMMF : Pointer;
    MIC_SEND : UINT;
    MIC_ONOFF : UINT;
    FOnReceive : TReceiveEvent;
    FOnConnect : TConnectEvent;
    OldOnMessage : TOnMessage;
    FName : string;
    function MMFLock : Boolean;
    procedure MMFUnlock;
    procedure MICReceiver(var msg: tagMsg; var Handled: Boolean);
  protected
  public
    constructor Create(AOwner : TComponent;Name : string;MMFBlockCount, MMFBlockSize : Cardinal);reintroduce;
    procedure AfterConstruction;override;
    procedure BeforeDestruction;override;
  published
    property ID : Byte read InstanceNumber;
    property Name : string read FName;
    function IsRunning(ID : Byte) : Boolean;
    procedure Send(DestID : Byte;var X;Size : Word);
    property OnReceive : TReceiveEvent read FOnReceive write FOnReceive;
    property OnConnect : TConnectEvent read FOnConnect write FOnConnect;
  end;

implementation

constructor TMutexMIC.Create(AOwner : TComponent;Name : string;MMFBlockCount, MMFBlockSize : Cardinal);
begin
  inherited Create(AOwner);
  FName:=Name;
  MMFBlock:=MMFBlockSize;
  if MMFBlock > 65536 then MMFBlock:=65536;
  MMFSize:=MMFBlockCount*MMFBlock;
  MIC_SEND:=RegisterWindowMessage(PChar('MIC_'+FName+'_SEND'));
  MIC_ONOFF:=RegisterWindowMessage(PChar('MIC_'+FName+'_ONOFF'));
end;

procedure TMutexMIC.AfterConstruction;
var
  a : Integer;
  b : Boolean;
  TempHandle : THandle;
begin
  //Eigener einmaliger Mutex
  InstanceNumber:=255;
  for a:=0 to 254 do
  begin
    InstanceRunning[a]:=True;
    TempHandle:=CreateMutex(nil,True,PChar('TMutexMIC-'+FName+'-MAIN-'+InttoStr(a)));
    if GetLastError = 0 then
    begin
      if InstanceNumber = 255 then
      begin
        InstanceNumber:=a;
        MainMutex:=TempHandle;
      end else
      begin
        InstanceRunning[a]:=False;
        CloseHandle(TempHandle);
      end;
    end else
      CloseHandle(TempHandle);
  end;
  if MainMutex = 0 then Halt;
  //Global anmelden
  PostMessage(HWND_BROADCAST,MIC_ONOFF,InstanceNumber,0);
  //MMF erstellen. Oder schon da?
  HMMF:=CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,MMFSize,PChar('TMutexMIC-'+FName+'-MMF'));
  b:=GetLastError <> ERROR_ALREADY_EXISTS;
  if HMMF = 0 then Halt;
  PMMF:=MapViewOfFile(HMMF,FILE_MAP_ALL_ACCESS,0,0,0);
  if PMMF = nil then
  begin
    CloseHandle(HMMF);
    Halt;
  end;
  if b then FillChar(PMMF^,MMFSize,0);
  //Lauscher auf!
  OldOnMessage:=Application.OnMessage;
  Application.OnMessage:=MICReceiver;
end;

procedure TMutexMIC.BeforeDestruction;
begin
  PostMessage(HWND_BROADCAST,MIC_ONOFF,InstanceNumber,1);
  UnMapViewOfFile(PMMF);
  CloseHandle(HMMF);
  CloseHandle(MainMutex);
end;

function TMutexMIC.IsRunning(ID : Byte) : Boolean;
begin
  Result:=InstanceRunning[ID];
end;

//MMF-Zugriffsmutex setzen, ggf. eine Sekunde warten
function TMutexMIC.MMFLock : Boolean;
begin
  Result:=True;
  LockMutex:=CreateMutex(nil,False,PChar('TMutexMIC-'+FName+'-LOCK'));
  if LockMutex = 0 then Result:=False else
  if WaitForSingleObject(LockMutex,1000) = WAIT_FAILED then Result:=False;
end;

//MMF-Zugriffsmutex löschen
procedure TMutexMIC.MMFUnlock;
begin
  ReleaseMutex(LockMutex);
  CloseHandle(LockMutex);
end;

procedure TMutexMIC.Send(DestID : Byte;var X;Size : Word);
var
  a, b : Integer;
  buf : array[0..3of Byte;
  w : Word;
  p : ^Byte;
begin
  if not InstanceRunning[DestID] then Exit;
  if Size > MMFBlock-4 then Exit;
  if MMFLock then
  begin
    //Datenpaket erstellen
    buf[0]:=InstanceNumber;
    buf[1]:=DestID;
    buf[2]:=Hi(Size);
    buf[3]:=Lo(Size);
    //Freien Platz im MMF finden und dort ablegen
    b:=MMFSize div MMFBlock-1;
    p:=PMMF;
    for a:=0 to b do
    begin
      Move(p^,w,2);
      if w = 0 then
      begin
        Move(buf,p^,4);
        Inc(p,4);
        Move(X,p^,Size);
        Break;
      end;
      Inc(p,MMFBlock);
    end;
    MMFUnlock;
    //Gegenüber benachrichtigen: "Sie haben Post"
    PostMessage(HWND_BROADCAST,MIC_SEND,InstanceNumber,DestID);
  end;
end;

//Wenn eine andere Instanz ruft ...
procedure TMutexMIC.MICReceiver(var msg: tagMsg; var Handled: Boolean);
var
  a, b : Integer;
  buf : array[0..3of Byte;
  dat : array of Byte;
  p : ^Byte;
  SourceID : Byte;
begin
  if (msg.message = MIC_SEND) and (msg.lParam = InstanceNumber) then
  begin
    if MMFLock then
    begin
      SourceID:=255;
      //Datenpakete mit eigener Zieladresse auslesen und aus dem MMF löschen
      b:=MMFSize div MMFBlock-1;
      p:=PMMF;
      for a:=0 to b do
      begin
        Move(p^,buf,4);
        if (buf[1] = InstanceNumber) and (buf[2]*256+buf[3] > 0then
        begin
          SourceID:=buf[0];
          SetLength(dat,buf[2]*256+buf[3]);
          Inc(p,4);
          Move(p^,dat[0],Length(dat));
          Dec(p,4);
          FillChar(buf,4,0);
          Move(buf,p^,4);
          Break;
        end;
        Inc(p,MMFBlock);
      end;
      MMFUnlock;
      if Assigned(FOnReceive) then FOnReceive(SourceID,dat[0],Length(dat));
    end;
    Handled:=True;
    Exit;
  end;
  if msg.message = MIC_ONOFF then
  begin
    if msg.lParam = 0 then InstanceRunning[msg.wParam]:=True else
      InstanceRunning[msg.wParam]:=False;
    if Assigned(FOnConnect) then FOnConnect(msg.wparam);
    Handled:=True;
    Exit;
  end;
  if Assigned(OldOnMessage) then OldOnMessage(msg,Handled);
end;

end.

Bei mir ist das ganze etwas einfacher gehalten, dafür schneller. Ich vertrete auch allgemein mehr das Pinzip "Keep it simple and small". :-)

Viel Spaß damit,
Jörg aka "Chef" im SDC


Boldar - Sa 27.12.08 19:31

mmh Benbe: Kannst du wohl mal ein Beispiel für die Benutzung geben, besonders in Bezu auf IPC bei globalen Hooks? Und wie sieht es bei der geschwindigkeit aus?