Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Problem beim Programmneustart mit Instanzkontrolle (https://www.delphipraxis.net/177445-problem-beim-programmneustart-mit-instanzkontrolle.html)

Opa Knack 7. Nov 2013 01:01

Delphi-Version: 2006

Problem beim Programmneustart mit Instanzkontrolle
 
Hallo,

mit folgender Unit stelle ich sicher, dass mein Programm nur einmal gestartet wird.

Delphi-Quellcode:
unit Startkontrolle;

interface

implementation

uses windows,Dialogs,sysutils;

var mHandle: THandle; // Mutexhandle

Initialization

      mHandle := CreateMutex(nil,True,'zeugnis.exe');

      if GetLastError = ERROR_ALREADY_EXISTS then
      begin
            showMessage('Es läuft bereits eine Instanz des Programms');
            Halt;
      end;

finalization

      if mHandle <> 0 then CloseHandle(mHandle)
end.
Im Programm selbst ergibt sich die Notwendigkeit, das Programm neu starten zu müssen. Hierzu verwende ich

Delphi-Quellcode:
form1.Close;
application.ProcessMessages;
ShellExecute(Application.Handle, 'open', Pchar(ParamStr(0)), nil, nil, sw_SHOWNORMAL);
Das Problem ist nun, dass beides sich nicht wirklich mag. Wenn ich neu starten lasse, läuft ja noch eine Instanz, wodurch die Startkontrolle sich beschwert und den Neustart abbricht.

Hat jemand eine Idee, wie ich das elegant lösen könnte?

Danke vorab
Opa

musicman56 7. Nov 2013 01:56

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Hallo,

ich hab mir für diesen Zweck einen kleinen Programmstarter geschrieben, der zwei Kommandozeilenparameter auswertet:

1. Das zu startende Programm

2. Die Wartezeit

Das kleine Progrämmchen ist übrigens auch nützlich, wenn man Programme im Windows-Autostart starten möchte, aber sichergehen will/muss, dass alle anderen Programme im Autostart schon vorher gestartet sind, weil z.B. eine Abhängigkeit besteht.

Übrigens...
Delphi-Quellcode:
  ...
  mHandle := CreateMutex(nil,True,ExtractFileName(ParamStr(0)));
  ...
... und die Unit funzt mit jeder Echse

Sir Rufo 7. Nov 2013 01:59

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Ich würde vor dem Aufruf des Programms den Mutex wieder freigeben ... :cyclops:

BTW

Den Code in finalization kannst du dir sparen, denn
Zitat:

Use the CloseHandle function to close the handle. The system closes the handle automatically when the process terminates. The mutex object is destroyed when its last handle has been closed.
schön ist auch
Zitat:

If you are using a named mutex to limit your application to a single instance, a malicious user can create this mutex before you do and prevent your application from starting. To prevent this situation, create a randomly named mutex and store the name so that it can only be obtained by an authorized user. Alternatively, you can use a file for this purpose. To limit your application to one instance per user, create a locked file in the user's profile directory.
Quelle: http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx

musicman56 7. Nov 2013 02:09

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Hi Sir Rufo,

sag mal, wann schläfst du denn? :)

Klar, die Unit hat noch Verbesserungspotential. Ich gebe den Mutex auch nicht frei. Habe es auch noch nirgendwo gesehen, muss aber nichts bedeuten. Ich dachte immer, bei Programmende wird der Mutex automatisch freigegeben?

Hier mal meine Variante:

Delphi-Quellcode:
unit RuMutex;

interface

uses Windows, SysUtils;

function CreateRubinSoftwareMutex(MutexName: string): boolean;
function CreateApplicationMutex: boolean;

implementation

function StringReplace(Value: string): string;
var
  i: integer;
begin
  Result := Value;
  for i := 1 to Length(Result) do
  if not CharInSet(Result[i],['0'..'9','a'..'z','A'..'Z'])
  then Result[i] := '_';
end;

function CreateRubinSoftwareMutex(MutexName: string): boolean;
var
  LastError   : DWord;
  SecurityDesc : TSecurityDescriptor;
  SecurityAttr : TSecurityAttributes;
  aName       : string;
  Res         : Cardinal;
begin
  Result := false;
  InitializeSecurityDescriptor(@SecurityDesc, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(@SecurityDesc, True, nil, False);
  SecurityAttr.nLength := SizeOf(SecurityAttr);
  SecurityAttr.lpSecurityDescriptor := @SecurityDesc;
  SecurityAttr.bInheritHandle := False;
  aName := 'Global\RUBiN_Software_' + StringReplace(MutexName);
  Res := CreateMutex(@SecurityAttr, True, PChar(aName));
  LastError := GetLastError;
  if (Res = 0) then raise EOSError.Create('Mutex Modul-Name <'
                         + aName+'> konnte nicht erzeugt werden. Fehler-Code:'
                         + IntToStr(LastError))
  else if (LastError <> ERROR_ALREADY_EXISTS) then Result := true;
end;

function CreateApplicationMutex: boolean;
begin
  Result := CreateRubinSoftwareMutex(ParamStr(0));
end;

end.

Sir Rufo 7. Nov 2013 02:22

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Wenn du das in eine Klasse und als Klassenmethoden verpackst wird das schon runder.

Ich kann da mal - nach meinem Schönheitsschlaf - einen Entwurf machen.

musicman56 7. Nov 2013 02:49

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Noch runder als rund....ich bin gespannt :-D
Brauch jetzt auch meinen Schönheitsschlaf.

sx2008 7. Nov 2013 05:40

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Also in der JCL gibt es die Klasse TJclAppInstances (Unit JclAppInst) die alle Probleme mit den Instanzen abdeckt.
Wenn man z.B. eine zweite Instanz startet obwohl nur eine erlaubt ist, dann gibt die 2. Instanz seine Aufrufparameter an die 1. Instanz weiter und beendet sich sich.
Sind mehrere Instanzen zugelassen können diese untereinander kommunizieren.

Wenn man das Rad nicht neu erfinden will ist diese Klasse die beste Lösung.

musicman56 7. Nov 2013 12:58

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Zitat:

Zitat von sx2008 (Beitrag 1234808)
Wenn man das Rad nicht neu erfinden will ist diese Klasse die beste Lösung.

Prinzipiell stimme ich dir zu. Wenn ich aber die JCL nicht installiert habe, stellt sich die Frage, ob man wegen einem Liter Milch eine Kuh kaufen sollte? :)

sx2008 7. Nov 2013 16:47

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Zitat:

Zitat von musicman56 (Beitrag 1234886)
Wenn ich aber die JCL nicht installiert habe, stellt sich die Frage, ob man wegen einem Liter Milch eine Kuh kaufen sollte? :)

Du trinkst wahrscheinlich viel mehr Milch als dir bewusst ist. (und die Kuh ist ja kostenlos)
Mal ernsthaft, jede Anwendung die über ein Hobbyprogrämmchen hinaus geht kann von der JCL profitieren.
Alles was Borland/Embacadero in der RTL vergessen hat findet man in der JCL.

Opa Knack 7. Nov 2013 23:47

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Den Bezug zwischen JCL und der Kuh übernehme ich dann mal in meinen Sprachgebrauch... :-)

Wenn ich das richtig verstehe (sorry, Musicman, aber in D2006 ist CharInSet nicht verfügbar, somit konnte ich das nicht ausprobieren), muss ich den Mutex freigeben, bevor ich ihn erzeuge (was ja auch logisch ist). Dann ergibt sich aber aus meiner Sicht das Problem, dass ich den Neustart ja vom Programm aus initiiere, der Mutex also noch besteht, und das dieser erst freigegeben werden kann, wenn das Programm auch beendet ist. Oder sehe ich das falsch?

Ich habe inzwischen ausprobiert, ob ich den Mutex dadurch freigeben kann, dass ich vor Programmende versuche, ihn neu zu erzeugen und bei Vorhandensein über CloseHandle freizugeben, leider jedoch ohne Erfolg.

Hat jemand noch eine andere praktikable Idee?

Dalai 8. Nov 2013 00:31

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Ich mache es so: der/das Mutex wird erst erzeugt, nachdem alle Programmparameter abgearbeitet sind. Die zweite Instanz wird mit einem bestimmten Parameter gestartet und wenn der gefunden wird, schließt die zweite Instanz die erste bereits laufende.

Mal in Code-Form (Auszug aus der .dpr):
Delphi-Quellcode:
begin
    if (ParamCount > 0) then
    begin
        Params:= ParamStr(1);
        if (Params = 'runas') then
        begin
            SendMessage(StrToInt(ParamStr(2)), WM_CLOSE, 0, 0);
        end;
    end;

    hMutex:= CreateMutex(nil, True, PRODUCTNAME + PRODUCTNAME + PRODUCTCOPYRIGHT);
    if GetLastError = ERROR_ALREADY_EXISTS then
    (* Weiterer hierfür nicht relevanter Code *)

    if hMutex <> 0 then
        CloseHandle(hMutex);
end.
Ich glaube, ParamStr(2) war die PID oder so, damit die Message nur an die laufende erste Instanz gesendet wird.

MfG Dalai

Sir Rufo 8. Nov 2013 00:41

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Zitat:

Zitat von Opa Knack (Beitrag 1235017)
Wenn ich das richtig verstehe (sorry, Musicman, aber in D2006 ist CharInSet nicht verfügbar, somit konnte ich das nicht ausprobieren), muss ich den Mutex freigeben, bevor ich ihn erzeuge (was ja auch logisch ist).

Das ist richtig
Zitat:

Zitat von Opa Knack (Beitrag 1235017)
Dann ergibt sich aber aus meiner Sicht das Problem, dass ich den Neustart ja vom Programm aus initiiere, der Mutex also noch besteht, und das dieser erst freigegeben werden kann, wenn das Programm auch beendet ist. Oder sehe ich das falsch?

Das siehst du richtig, dass du das falsch siehst ;)
Zitat:

Zitat von Opa Knack (Beitrag 1235017)
Ich habe inzwischen ausprobiert, ob ich den Mutex dadurch freigeben kann, dass ich vor Programmende versuche, ihn neu zu erzeugen und bei Vorhandensein über CloseHandle freizugeben, leider jedoch ohne Erfolg.

Du hast vom Seil etwas abgeschnitten und festgestellt, dass es immer noch zu kurz ist?
Zitat:

Zitat von Opa Knack (Beitrag 1235017)
Hat jemand noch eine andere praktikable Idee?

Ja, gib den Mutex vor dem Aufruf von
Delphi-Quellcode:
ShellExecute
mit
Delphi-Quellcode:
CloseHandle
frei.

Dann wird das Handle geschlossen (übersetzt heißt CloseHandle -> SchließeHandle).
Wird der Prozess beendet, dann werden alle noch offenen Handles von diesem Prozess automatisch geschlossen.
Aber in diesem Falle musst du den halt vorher manuell freigeben.

Delphi-Quellcode:
  // Nun ja ... wenn schon dann bitte mit Self
  {form1}Self.Close;
  // Da wir aber die Anwendung schließen wollen, müssen wir das HauptFormular schließen, also besser
  Application.MainForm.Close;

  // wozu das? unnötig!
  // application.ProcessMessages;

  // Hier jetzt das Handle vom Mutex freigeben
  CloseHandle( MyAppMutexHandle );

  // Nicht das Application.Handle übergeben sondern 0
  ShellExecute({Application.Handle} 0, 'open', Pchar(ParamStr(0)), nil, nil, sw_SHOWNORMAL);

Sir Rufo 8. Nov 2013 01:37

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Hier die versprochene Klasse
Delphi-Quellcode:
unit AppMutex;

interface

type
  TAppMutexStrategy = class abstract
  private
    procedure SetActive( const Value : Boolean );
  protected
    function GetActive : Boolean; virtual; abstract;
    procedure AquireMutex; virtual; abstract;
    procedure ReleaseMutex; virtual; abstract;
  public
    destructor Destroy; override;
  end;

  TAppMutex = class
  private
    class var FStrategy : TAppMutexStrategy;
  private
    class procedure SetActive( const Value : Boolean ); static;
    class function GetActive : Boolean; static;
    class destructor Destroy;
  public
    class property Active : Boolean read GetActive write SetActive;
    class procedure SetStrategy( AStrategy : TAppMutexStrategy );
  end;

  TNamedAppMutexStrategy = class( TAppMutexStrategy )
  private
    FHandle : Cardinal;
    FName :  string;
  protected
    procedure AquireMutex; override;
    procedure ReleaseMutex; override;
    function GetActive : Boolean; override;
    function GetName : string; virtual;
    property Name : string read GetName;
  public
    constructor Create( const AName : string );
  end;

  TLocalAppMutexStrategy = class( TNamedAppMutexStrategy )
  protected
    function GetName : string; override;
  end;

  TGlobalAppMutexStrategy = class( TNamedAppMutexStrategy )
  protected
    function GetName : string; override;
  end;

implementation

uses
  Windows,
  SysUtils;

{ TAppMutex }

class destructor TAppMutex.Destroy;
begin
  FreeAndNil( FStrategy );
end;

class function TAppMutex.GetActive : Boolean;
begin
  Result := FStrategy.GetActive;
end;

class procedure TAppMutex.SetActive( const Value : Boolean );
begin
  FStrategy.SetActive( Value );
end;

class procedure TAppMutex.SetStrategy( AStrategy : TAppMutexStrategy );
begin
  if Assigned( FStrategy )
  then
    FreeAndNil( FStrategy );

  FStrategy := AStrategy;
end;

{ TAppMutexStrategy }

destructor TAppMutexStrategy.Destroy;
begin
  SetActive( False );
  inherited;
end;

procedure TAppMutexStrategy.SetActive( const Value : Boolean );
begin
  if Value = GetActive
  then
    Exit;

  if Value
  then
    AquireMutex
  else
    ReleaseMutex;
end;

{ TNamedAppMutexStrategy }

procedure TNamedAppMutexStrategy.AquireMutex;
var
  LLastError : Cardinal;
begin
  FHandle := CreateMutex( nil, True, PChar( Name ) );

  LLastError := GetLastError;

  if LLastError = ERROR_ALREADY_EXISTS
  then
    begin
      CloseHandle( FHandle );
      FHandle := 0;
    end;
end;

constructor TNamedAppMutexStrategy.Create( const AName : string );
begin
  inherited Create;
  FName := AName;
end;

function TNamedAppMutexStrategy.GetActive : Boolean;
begin
  Result := ( FHandle <> 0 );
end;

function TNamedAppMutexStrategy.GetName : string;
var
  LIdx : Integer;
begin
  Result  := FName;
  for LIdx := 1 to Length( Result ) do
    begin
      if not CharInSet( Result[LIdx], ['0' .. '9', 'A' .. 'Z', 'a' .. 'z', '-'] )
      then
        Result[LIdx] := '_';
    end;
end;

procedure TNamedAppMutexStrategy.ReleaseMutex;
begin
  CloseHandle( FHandle );
  FHandle := 0;
end;

{ TLocalAppMutexStrategy }

function TLocalAppMutexStrategy.GetName : string;
begin
  Result := 'Local\' + inherited;
end;

{ TGlobalAppMutexStrategy }

function TGlobalAppMutexStrategy.GetName : string;
begin
  Result := 'Global\' + inherited;
end;

end.
Im Einsatz so
Delphi-Quellcode:
program MutexTest;

uses
  Forms,
  MainView_Form in 'MainView_Form.pas' {MainView} ,
  AppMutex in 'AppMutex.pas',
  AlertView_Form in 'AlertView_Form.pas' {AlertView};

{$R *.res}

begin
  // Strategie einstellen
  TAppMutex.SetStrategy( TGlobalAppMutexStrategy.Create( '6D274674-150A-490E-B8D0-726C6D556F29' ) );
  // Aktivieren
  TAppMutex.Active := True;

  // Überprüfung
  if not TAppMutex.Active
  then
  begin
    TAlertView.Create( nil ).ShowModal;
    Halt;
  end;

  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm( TMainView, MainView );
  Application.Run;

end.

musicman56 8. Nov 2013 10:04

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Hallo,

vielen Dank Sir Rufo, ich werd's mal ausprobieren :thumb:

hathor 8. Nov 2013 19:13

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Das Problem "OneInstance" hatte ich heute auch und habe es im DPR-File gelöst.
Wenn das zuerst gestartete Programm minimiert ist, wird es angezeigt.
Funktioniert auch unter WIN 8.0.


Delphi-Quellcode:
program TEST1;

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

{$R *.res}

procedure SwitchToThisWindow(h1: hWnd; x: bool); stdcall; external user32 Name
'SwitchToThisWindow'; {x = false: Size unchanged, x = true: normal size}

var
  PreviousHandle : THandle;
begin
  PreviousHandle := Windows.FindWindow(NIL,'MyProg'); //MyProg = Form1.caption
  if PreviousHandle = 0 then
  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end
  else
 begin
    if Windows.IsIconic(PreviousHandle) then
        Windows.ShowWindow(PreviousHandle, SW_RESTORE);
    SwitchToThisWindow(PreviousHandle, TRUE);
    SetForegroundWindow(PreviousHandle);
    SetWindowPos(PreviousHandle,
      HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
 end;
end.

Opa Knack 9. Nov 2013 01:12

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Danke für die hilfreichen Tipps, ich werde mich hineinknien. Übrigens funktioniert es mit
Delphi-Quellcode:
  // Nun ja ... wenn schon dann bitte mit Self
  {form1}Self.Close;
  // Da wir aber die Anwendung schließen wollen, müssen wir das HauptFormular schließen, also besser
  Application.MainForm.Close;

  // wozu das? unnötig!
  // application.ProcessMessages;

  // Hier jetzt das Handle vom Mutex freigeben
  CloseHandle( MyAppMutexHandle );

  // Nicht das Application.Handle übergeben sondern 0
  ShellExecute({Application.Handle} 0, 'open', Pchar(ParamStr(0)), nil, nil, sw_SHOWNORMAL);
nicht. Selbst wenn man MyAppMutexHandle (das logischerweise nicht vorhanden ist) durch den eigentlichen Mutex ersetzt.

Sir Rufo 9. Nov 2013 01:20

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Zitat:

Zitat von Opa Knack (Beitrag 1235200)
Danke für die hilfreichen Tipps, ich werde mich hineinknien. Übrigens funktioniert es mit
Delphi-Quellcode:
...
nicht. Selbst wenn man MyAppMutexHandle (das logischerweise nicht vorhanden ist) durch den eigentlichen Mutex ersetzt.

Dann machst du irgendwas noch falsch, denn mit dem Freigeben des Mutex-Handle kann dieser Mutex wieder belegt werden.
(getestet)

Ähm, ja logisch ist das nicht bekannt - hätte ich besser schreiben sollen
Delphi-Quellcode:
DatAppMutexDingensBumsHandleWatDuDaVerstecktInDerUnitRumliegenHast
;)

Da du folgenden Code in deiner unit hast
Delphi-Quellcode:

if mHandle <> 0 then CloseHandle(mHandle)
gehe ich auch davon aus, dass du weißt wie man ein Handle freigibt und dass man dazu auch das Handle selber braucht ;)

Furtbichler 9. Nov 2013 09:53

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Zitat:

Zitat von hathor (Beitrag 1235160)
...
Delphi-Quellcode:
...
  PreviousHandle := Windows.FindWindow(NIL,'MyProg'); //MyProg = Form1.caption

Das ist nicht allgemeingültig, denn viele Programme haben im Titel des Hauptformulars variable Informationen (z.B. den Namen der aktuell geladenen Datei, User- oder DB-infos etc.), d.h. Du weißt beim Programmstart nicht immer, was im Titel des Hauptformulars steht. Zudem musst Du deinen Code bei jeder Verwendung individuell anpassen.

Zum Thema 'eine Instanz und Neustart' wollte ich noch meinen Senf dazugeben:

Das Kürzeste, was ich diesbezüglich auf meinem Sperrmüll gefunden habe, ist eine Unit, die man einfach per 'uses' einbindet und dadurch einen Mehrfachstart verhindert.
Delphi-Quellcode:
unit OneInstance;
interface
implementation
uses Windows, SysUtils;

var
  hMutex: Cardinal;

initialization
  hMutex := CreateMutex(nil, True, PChar(UpperCase(ExtractFileName(ParamStr(0)))));
  if GetLastError = ERROR_ALREADY_EXISTS then Halt;
end.
Ich persönlich halte das für vollkommen ausreichend.

Mit 'Restart' wäre das diese Kleinstunit (Eben reingefrickelt):
Delphi-Quellcode:
unit OneInstance;
interface
Procedure Restart;
implementation
uses Windows, SysUtils, ShellApi;

var
  hMutex: Cardinal;

Procedure Restart;
Begin
  CloseHandle(hMutex);
  ShellExecute({Application.Handle} 0, 'open', Pchar(ParamStr(0)), nil, nil, sw_SHOWNORMAL);
  Halt; // Brutalstmögliches Beenden der eigenen Anwendung.
End;

initialization
  hMutex := CreateMutex(nil, True, PChar(UpperCase(ExtractFileName(ParamStr(0)))));
  if GetLastError = ERROR_ALREADY_EXISTS then Halt;
end.
Allerdings starten sich meine Programme nicht neu. Ich halte das für falsch. Ein Programm hat eine bestimmte Aufgabe und wenn es sie nicht erfüllen kann, dann soll sich das Programm beenden.

Wenn ich eine 24/7-Umgebung benötige, sorge ich (z.B. durch eine BAT-Datei), das die Anwendung neu gestartet wird.
Code:
:label
MyProg.exe
goto label
So kann ich einerseits die Anwendung in einer Endlosschleife am Leben halten, andererseits die gleiche Anwendung ohne Klimmzüge laufen lassen, bis sie abschmiert. Falls sie nun ständig abschmiert, kann ich die Endlosschleife einfach unterbrechen, indem ich die Batch-Datei beende (das Fenster schließe). Mit Selbstneustart benötige ich den Taskmanager, auf den ich aber auch nicht immer Zugriff habe.

Ich will nicht sagen, das das besser ist, aber es ist -für mich- so einfacher. In kritischen Umgebungen habe ich einen Sentinel, also eine Anwendung, die im Hintergrund bestimmte Prozesse überwacht und periodisch prüft, ob diese ansprechbar oder 'abgekackt' sind, d.h. hängen. Wenn ja, wird der Prozess gekillt und die Anwendung/Prozess neu gestartet. Eine zu überwachende Anwendung kann dem Sentinel auch sagen, das sie für immer sich beenden möchte, sodaß dieser die Überwachung einstellt.

So richtig hat mir das alles nie gefallen, denn eine Anwendung sollte immer laufen können. Leider geht das nicht immer (schlechte Treiber, externe DLL usw.)

Senfertig.

hathor 9. Nov 2013 11:19

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Ich habe mein DPR-File etwas geändert - vielleicht kann es jemand brauchen:

Delphi-Quellcode:
program MyProg;

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

{$R *.res}

procedure SwitchToThisWindow(h1: hWnd; x: bool); stdcall; external user32 Name
'SwitchToThisWindow'; {x = false: Size unchanged, x = true: normal size}

var
  hMutex: Cardinal;
  PreviousHandle : THandle;

BEGIN
  hMutex := CreateMutex(nil, True, PChar(UpperCase(ExtractFileName(ParamStr(0)))));
  if GetLastError = ERROR_ALREADY_EXISTS then
  begin
  PreviousHandle := Windows.FindWindow(NIL,PChar(UpperCase(ExtractFileName(ParamStr(0)))));
    if Windows.IsIconic(PreviousHandle) then
                Windows.ShowWindow(PreviousHandle, SW_RESTORE);
    SwitchToThisWindow(PreviousHandle, TRUE);
    SetForegroundWindow(PreviousHandle);
    SetWindowPos(PreviousHandle,
                HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
  end else
  begin
    Application.Initialize;
    Application.MainFormOnTaskbar := True;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end;
end.

musicman56 9. Nov 2013 15:10

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Hallo,

Delphi-Quellcode:
hMutex := CreateMutex(nil, True, PChar(UpperCase(ExtractFileName(ParamStr(0)))));
wie ist das eigentlich, wenn ParamStr(0) Sonderzeichen wie "(" ")" "&", Leerzeichen usw. enthält. Funktioniert der Mutex dann noch richtig?

Was mir außerdem nicht ganz optimal erscheint ist die Tatsache, dass mit ParamStr(0) zwar verhindert wird, dass dieselbe Exe zeimal gestartet wird, aber nicht eine weitere Instanz desselben Programmes in einem anderen Ordner. Das zu verhindern kommt bei mir zumindest sehr häufig vor.

Es ist der Grund dafür, dass ich in meiner Unit zwei Möglichkeiten habe, den Mutex zu erzeugen: Einmal mit ParamStr(0) wenn mich dieser Sachverhalt nicht stört, und zusätzlich eine Funktion mit vorgegebenem Mutexnamen, den ich meistens von der Applikation ableite, zum Beispiel dem Application.Title.

hathor 9. Nov 2013 19:35

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Zitat:

Zitat von musicman56 (Beitrag 1235242)
Hallo,

Delphi-Quellcode:
hMutex := CreateMutex(nil, True, PChar(UpperCase(ExtractFileName(ParamStr(0)))));
...Was mir außerdem nicht ganz optimal erscheint ist die Tatsache, dass mit ParamStr(0) zwar verhindert wird, dass dieselbe Exe zeimal gestartet wird, aber nicht eine weitere Instanz desselben Programmes in einem anderen Ordner. Das zu verhindern kommt bei mir zumindest sehr häufig vor.

Label1.Caption:= UpperCase(ExtractFileName(ParamStr(0)));
ergibt NUR den Programm-Namen OHNE Pfad, deshalb kann das Programm nicht 2x gestartet werden.

musicman56 10. Nov 2013 13:04

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Sorry, du hast natürlich recht. Das "UpperCase(ExtractFileName(..." habe ich übersehen. :evil:

Furtbichler 10. Nov 2013 14:00

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Das ist unpraktisch, weil meine Programme alle 'Project1.EXE' heißen. :wall:

musicman56 10. Nov 2013 18:49

AW: Problem beim Programmneustart mit Instanzkontrolle
 
Das macht aber nix, weil die ja jeweils in einem anderen Ordner sein müssen :duck:


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:19 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