![]() |
Delphi-Version: 2006
Problem beim Programmneustart mit Instanzkontrolle
Hallo,
mit folgender Unit stelle ich sicher, dass mein Programm nur einmal gestartet wird.
Delphi-Quellcode:
Im Programm selbst ergibt sich die Notwendigkeit, das Programm neu starten zu müssen. Hierzu verwende ich
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.
Delphi-Quellcode:
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.
form1.Close;
application.ProcessMessages; ShellExecute(Application.Handle, 'open', Pchar(ParamStr(0)), nil, nil, sw_SHOWNORMAL); Hat jemand eine Idee, wie ich das elegant lösen könnte? Danke vorab Opa |
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:
... und die Unit funzt mit jeder Echse
...
mHandle := CreateMutex(nil,True,ExtractFileName(ParamStr(0))); ... |
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:
Zitat:
![]() |
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. |
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. |
AW: Problem beim Programmneustart mit Instanzkontrolle
Noch runder als rund....ich bin gespannt :-D
Brauch jetzt auch meinen Schönheitsschlaf. |
AW: Problem beim Programmneustart mit Instanzkontrolle
Also in der
![]() 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. |
AW: Problem beim Programmneustart mit Instanzkontrolle
Zitat:
|
AW: Problem beim Programmneustart mit Instanzkontrolle
Zitat:
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. |
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? |
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:
Ich glaube, ParamStr(2) war die PID oder so, damit die Message nur an die laufende erste Instanz gesendet wird.
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. MfG Dalai |
AW: Problem beim Programmneustart mit Instanzkontrolle
Zitat:
Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
mit
ShellExecute
Delphi-Quellcode:
frei.
CloseHandle
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); |
AW: Problem beim Programmneustart mit Instanzkontrolle
Hier die versprochene Klasse
Delphi-Quellcode:
Im Einsatz so
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.
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. |
AW: Problem beim Programmneustart mit Instanzkontrolle
Hallo,
vielen Dank Sir Rufo, ich werd's mal ausprobieren :thumb: |
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. |
AW: Problem beim Programmneustart mit Instanzkontrolle
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.
// 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); |
AW: Problem beim Programmneustart mit Instanzkontrolle
Zitat:
(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:
gehe ich auch davon aus, dass du weißt wie man ein Handle freigibt und dass man dazu auch das Handle selber braucht ;)if mHandle <> 0 then CloseHandle(mHandle) |
AW: Problem beim Programmneustart mit Instanzkontrolle
Zitat:
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:
Ich persönlich halte das für vollkommen ausreichend.
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. Mit 'Restart' wäre das diese Kleinstunit (Eben reingefrickelt):
Delphi-Quellcode:
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.
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. Wenn ich eine 24/7-Umgebung benötige, sorge ich (z.B. durch eine BAT-Datei), das die Anwendung neu gestartet wird.
Code:
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.
:label
MyProg.exe goto label 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. |
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. |
AW: Problem beim Programmneustart mit Instanzkontrolle
Hallo,
Delphi-Quellcode:
wie ist das eigentlich, wenn ParamStr(0) Sonderzeichen wie "(" ")" "&", Leerzeichen usw. enthält. Funktioniert der Mutex dann noch richtig?
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. 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. |
AW: Problem beim Programmneustart mit Instanzkontrolle
Zitat:
ergibt NUR den Programm-Namen OHNE Pfad, deshalb kann das Programm nicht 2x gestartet werden. |
AW: Problem beim Programmneustart mit Instanzkontrolle
Sorry, du hast natürlich recht. Das "UpperCase(ExtractFileName(..." habe ich übersehen. :evil:
|
AW: Problem beim Programmneustart mit Instanzkontrolle
Das ist unpraktisch, weil meine Programme alle 'Project1.EXE' heißen. :wall:
|
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 22:08 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz