Hallo,
ich habe vor kurzem den FAQ Beitrag
...verhindern dass die Anwendung mehrmals gestartet wird? eingestellt, wo ich beschrieben habe, wie man verhindert, dass eine Anwendung mehrmals gestartet wird, und wie man außerdem dafür sorgt, dass beim starten einer neuen Instanz die alte Instanz angezeigt wird.
Jetzt habe ich den vorhandenen Code so erweitert, dass die neu gestartete Instanz Daten an die laufende Instanz schicken kann. Für dieses Beispiel werde ich einfach die Startparameter der neuen Instanz übergeben. [meta]Prozess, Kommunikation, Interprozess-Kommunikation, Daten austauschen, Datenaustausch[/meta]
Zuerst mal das wichtigste: wie übergeben wir die Daten? Man könnte einen Zeiger übergeben, aber leider ist das unter NT problematisch. Also habe ich mich für MMF - Memory Mapped Files - entschieden.
Was ist MMF? Wie den Name schon sagt, das ist eine Datei, die im Speicher abgelegt ist. Eine feine Sache, so kann man z.B. Gigabytegroße Dateien Stückchenweise in dem RAM laden, um auf jeden Abschnitt schnelle Zugriff zu haben. Das wollen wir aber nicht - uns interessiert nur die Tatsache, dass wir von mehreren Prozessen auf ein MMF-Objekt zugriff haben können.
Also, hier erstmal die erste von 2 Prozeduren:
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:
| procedure WriteStringToMMF(MMFName, Str: string); var Map: Cardinal; Data: Pointer; begin Map := CreateFileMapping( INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, Length(Str) + 4, PChar(MMFName)); if (Map <> 0) then begin Data := MapViewOfFile( Map, FILE_MAP_WRITE, 0, 0, 0); if (Assigned(Data)) then begin PCardinal(Data)^ := Length(Str); CopyMemory(Pointer(Cardinal(Data) + 4), Pointer(Str), Length(Str));
UnmapViewOfFile(Data); end; end; end; |
CreateFileMapping() erzeugt das MMF-Objekt. Der erste Parameter ist das Handle einer Datei - wir brauchen keine Ausgangsdatei, also geben wir INVALID_HANDLE_VALUE an, und erhalten ein reines "RAM-Objekt". Dann mappen wir das MMF - d.h. wir sagen, welchen Ausschnitt des MMF wir gerne zum schreiben öffnen wollen (wir öffnen alle Daten auf einmal). Dann schreiben wir den String rein - natürlich kann man jegliche Art von Daten reinschreiben.
MMFName ist übrigens ein String, der das MMF-Objekt identifiziert, so wie bei einem Mutex oder eine Semaphore. Übrigens: Ein Mutex darf nicht den selben Namen wie eine Semaphroe haben und auch nicht den gleichen wie ein MMF!
So, dann wollen wir das auch nch auslesen:
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:
| function ReadStringFromMMF(MMFName: string): string; var Map: Cardinal; Data: Pointer; Len: Cardinal; begin Map := OpenFileMapping( FILE_MAP_READ, True, PChar(MMfName)); if (Map <> 0) then begin Data := MapViewOfFile( Map, FILE_MAP_READ, 0, 0, 0); if (Data <> nil) then begin Len := PCardinal(Data)^; SetLength(Result, Len); CopyMemory(Pointer(Result), Pointer(Cardinal(Data) + 4), Len);
UnmapViewOfFile(Data); CloseHandle(Map); end; end; end; |
Alles klar, oder? Der Rückgabewert ist der String, den die erste Prozedur in das MMF geschrieben hat.
So, das jetzt in ein vorhandenes Projekt. Ich setzte mein vorangegangenes Tutorial zum verhindern mehrere Instanzen als Grundwissen vorraus. Hier die DPR:
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:
| program Instanz;
uses Forms, Windows, Instanz1 in 'Instanz1.pas' ;
{$R *.res}
var Semaphore: THandle;
procedure WriteStringToMMF(MMFName, Str: string); var Map: Cardinal; Data: Pointer; begin Map := CreateFileMapping( INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, Length(Str) + 4, PChar(MMFName)); if (Map <> 0) then begin Data := MapViewOfFile( Map, FILE_MAP_WRITE, 0, 0, 0); if (Assigned(Data)) then begin PCardinal(Data)^ := Length(Str); CopyMemory(Pointer(Cardinal(Data) + 4), Pointer(Str), Length(Str));
UnmapViewOfFile(Data); end; end; end;
begin ActivationMessage := RegisterWindowMessage(PChar(MyGUID)); Semaphore := CreateSemaphore(nil, 1, 1, PChar(MyGUID));
if (GetLastError = ERROR_ALREADY_EXISTS) then begin WriteStringToMMF(MyGUID + 'MMF', CmdLine); PostMessage(HWND_BROADCAST, ActivationMessage, 0, 0); Exit; end;
Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run;
CloseHandle(Semaphore); end. |
Und die PAS:
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:
| unit Instanz1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, AppEvnts, StdCtrls;
type TForm1 = class(TForm) ApplicationEvents1: TApplicationEvents; Memo1: TMemo; procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); private public end;
const MyGUID = '{1F14549B-3399-4413-A5DB-E19B10652D86}';
var ActivationMessage: Cardinal;
var Form1: TForm1;
implementation
{$R *.dfm}
function ReadStringFromMMF(MMFName: string): string; var Map: Cardinal; Data: Pointer; Len: Cardinal; begin Map := OpenFileMapping( FILE_MAP_READ, True, PChar(MMfName)); if (Map <> 0) then begin Data := MapViewOfFile( Map, FILE_MAP_READ, 0, 0, 0); if (Data <> nil) then begin Len := PCardinal(Data)^; SetLength(Result, Len); CopyMemory(Pointer(Result), Pointer(Cardinal(Data) + 4), Len);
UnmapViewOfFile(Data); CloseHandle(Map); end; end; end;
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); begin if (Msg.message = ActivationMessage) then begin Memo1.Lines.Add(ReadStringFromMMF(MyGUID + 'MMF'));
Application.Restore; SetForegroundWindow(Handle); Handled := True; end; end;
end. |
Das Formular ("Form1") hat ein Memo namens "Memo1" und besitzt die TApplicationEvents Komponenten mit dem Namen "ApplicationEvents1". Das OnMessage Event dieser Komponente muss mit der ApplicationEvents1Message Procedure verbunden werden.
Schleichwerbung... eine Demo gibt es auch hier:
www.delphistuff.gmxh...downlods/Instanz.zip
PS: Ich weiß nicht 100%ig, ob das MMF-Objekt auch zuverlässig freigegeben wird, müsste es aber meiner Meinung nach. Wenn das nicht so ist - posten! Danke!
Moderiert von Christian S.: Meta-Tags und - Wörter eingefügt