Autor Beitrag
Andreas Pfau
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 997



BeitragVerfasst: Di 13.01.04 17:50 
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:
ausblenden volle Höhe 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:
procedure WriteStringToMMF(MMFName, Str: string);
var
  Map: Cardinal;
  Data: Pointer;
begin
  // MMF-Objekt erzeugen
  Map := CreateFileMapping(
    INVALID_HANDLE_VALUE,    // Keine Quelldatei
    nil,                     // Keine Sicherheitsattribute
    PAGE_READWRITE,          // Voller Zugriff
    0,                       // Minimalgröße
    Length(Str) + 4,         // Maximalgröße
    PChar(MMFName));         // Name des MMF-Objekts

  if (Map <> 0then
  begin
    // MMF-Objekt mappen
    // Dadurch haben wir erst Zugriff auf die Daten
    Data := MapViewOfFile(
      Map,                   // Handle des MMF-Objekts
      FILE_MAP_WRITE,        // Schreibzugriff
      0,                     // Kein Start-Offset
      0,                     // Kein Start-Offset
      0);                    // Gesamte Größe mappen

    if (Assigned(Data)) then
    begin
      // String-Länge in MMF schreiben
      PCardinal(Data)^ := Length(Str);
      // String in MMF schreiben
      CopyMemory(Pointer(Cardinal(Data) + 4), Pointer(Str), Length(Str));

      // Mapping schließen
      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:
ausblenden volle Höhe 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:
function ReadStringFromMMF(MMFName: string): string;
var
  Map: Cardinal;
  Data: Pointer;
  Len: Cardinal;
begin
  // MMF-Objekt öffnen
  Map := OpenFileMapping(
    FILE_MAP_READ,           // Lesezugriff
    True,                    // Handle vererben
    PChar(MMfName));         // Name des MMF-Objekts

  if (Map <> 0then
  begin
    // MMF-Objekt mappen
    Data := MapViewOfFile(
      Map,                   // Handle des MMF-Objekts
      FILE_MAP_READ,         // Lesezugriff
      0,                     // Kein Start-Offset
      0,                     // Kein Start-Offset
      0);                    // Gesamte Größe mappen

    if (Data <> nilthen
    begin
      // String-Länge auslesen
      Len := PCardinal(Data)^;
      // String-Länge setzen (Speicher alloziieren)
      SetLength(Result, Len);
      // String auslesen
      CopyMemory(Pointer(Result), Pointer(Cardinal(Data) + 4), Len);

      // Mapping schließen
      UnmapViewOfFile(Data);
      // MMF-Objekt freigeben
      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:
ausblenden volle Höhe 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:
program Instanz;

uses
  Forms,
  Windows,
  Instanz1 in 'Instanz1.pas' {Form1};

{$R *.res}

var
  Semaphore: THandle;

procedure WriteStringToMMF(MMFName, Str: string);
var
  Map: Cardinal;
  Data: Pointer;
begin
  // MMF-Objekt erzeugen
  Map := CreateFileMapping(
    INVALID_HANDLE_VALUE,    // Keine Quelldatei
    nil,                     // Keine Sicherheitsattribute
    PAGE_READWRITE,          // Voller Zugriff
    0,                       // Minimalgröße
    Length(Str) + 4,         // Maximalgröße
    PChar(MMFName));         // Name des MMF-Objekts

  if (Map <> 0then
  begin
    // MMF-Objekt mappen
    // Dadurch haben wir erst Zugriff auf die Daten
    Data := MapViewOfFile(
      Map,                   // Handle des MMF-Objekts
      FILE_MAP_WRITE,        // Schreibzugriff
      0,                     // Kein Start-Offset
      0,                     // Kein Start-Offset
      0);                    // Gesamte Größe mappen

    if (Assigned(Data)) then
    begin
      // String-Länge in MMF schreiben
      PCardinal(Data)^ := Length(Str);
      // String in MMF schreiben
      CopyMemory(Pointer(Cardinal(Data) + 4), Pointer(Str), Length(Str));

      // Mapping schließen
      UnmapViewOfFile(Data);
    end;
  end;
end;

begin
  ActivationMessage := RegisterWindowMessage(PChar(MyGUID));
  Semaphore := CreateSemaphore(nil11, PChar(MyGUID));

  // Prüfen ob die Semaphore bereits existierte
  if (GetLastError = ERROR_ALREADY_EXISTS) then
  begin
    WriteStringToMMF(MyGUID + 'MMF', CmdLine);
    PostMessage(HWND_BROADCAST, ActivationMessage, 00);
    Exit;
  end;

  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;

  CloseHandle(Semaphore);
end.

Und die PAS:
ausblenden volle Höhe 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:
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
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  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
  // MMF-Objekt öffnen
  Map := OpenFileMapping(
    FILE_MAP_READ,           // Lesezugriff
    True,                    // Handle vererben
    PChar(MMfName));         // Name des MMF-Objekts

  if (Map <> 0then
  begin
    // MMF-Objekt mappen
    Data := MapViewOfFile(
      Map,                   // Handle des MMF-Objekts
      FILE_MAP_READ,         // Lesezugriff
      0,                     // Kein Start-Offset
      0,                     // Kein Start-Offset
      0);                    // Gesamte Größe mappen

    if (Data <> nilthen
    begin
      // String-Länge auslesen
      Len := PCardinal(Data)^;
      // String-Länge setzen (Speicher alloziieren)
      SetLength(Result, Len);
      // String auslesen
      CopyMemory(Pointer(Result), Pointer(Cardinal(Data) + 4), Len);

      // Mapping schließen
      UnmapViewOfFile(Data);
      // MMF-Objekt freigeben
      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 user profile iconChristian S.: Meta-Tags und - Wörter eingefügt

_________________
Life is a bad adventure, but the graphic is really good!