Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi [Unit für Delphi >= 2010] Eigene Anwendung neu starten (https://www.delphipraxis.net/162066-%5Bunit-fuer-delphi-%3D-2010%5D-eigene-anwendung-neu-starten.html)

Björn Ole 4. Aug 2011 12:24

[Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Liste der Anhänge anzeigen (Anzahl: 1)
Huhu,

ich möchte hier mal eine Unit zur Verfügung stellen, die das Neustarten der eigenen Anwendung sehr leicht gestaltet. Als Nebeneffekt kann es auch nur maximal eine laufende Instanz der Anwendung geben.

Zuerst muss die Unit in die uses Liste der .dpr (Project -> View Source) am besten als erstes eingebunden werden. Danach sollte noch mittels Strg+Shift+G eine GUID erzeugt und mit der vorhandenen ersetzt werden. Anschließen muss die Unit zusätzlich noch dort eingebunden werden, wo die Restart Methode aufgerufen werden soll, also z.B. in der Unit1.pas.

Kleines Beispiel:

Die .dpr
Delphi-Quellcode:
program Project1;

uses
  // Unit einbinden \\
  uApplicationRestarter in 'uApplicationRestarter.pas',
  // Alle weiteren Units ... \\
  Forms,
  Unit1 in 'Unit1.pas' {Form1} ,

{$R *.res}

begin
  // Diese zwei Zeilen einfügen und GUID abändern. \\
  if not TApplicationRestarter.Initialize(Application.Handle, '{33EB3B5C-9A2C-4E85-805C-FFD49C1AD468A}') then
    Exit;
  // Ab hier gehts normal weiter \\

  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;

end.
Will man neu starten, dann einfach so:
Delphi-Quellcode:
uses uApplicationRestarter;

{...}

procedure TForm1.Button1Click(Sender: TObject);
begin
  TApplicationRestarter.Restart;
end;
Restart() gibt einen Boolean zurück, bei False kann man mittels GetLastError mit dem genauen Fehlergrund weiterarbeiten. Das Ganze geht leider erst ab Delphi 2010. Die Unit hab ich unter XE geschrieben und getestet, sollte aber auch unter 2010 laufen. Werde ich in den kommenden Tagen testen.

Für Verbesserungsvorschläge wär ich sehr dankbar. :thumb:

chaosben 4. Aug 2011 13:09

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Danke fürs Teilen. :)

Ich persönlich würde folgende Vorgehensweise für "schöner" befinden: Der Restarter ist ein class helper für TApplication und initialisiert sich selbst über die init-Routine der Unit.
Das hätte den Vorteil, das sich der "Aufwand" auf das Einbinden der Unit beschränkt und es ganz easy per
Delphi-Quellcode:
Application.Restart
nutzbar wäre.
Außerdem könnten dann auch User mit Delphis unter 2010 darüber freuen.

CCRDude 4. Aug 2011 13:11

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Hmmm... ok, ein paar hoffentlich konstruktive Vorschläge:

1. Warum "if ParamStr(1)=" und nicht "FindCmdLineSwitch()"?
2. Wenn bereits eine Instanz läuft, solltest Du die Parameter an diese weitergeben. Z.B. per Named Pipe.
3. Für Vista und später solltest Du eine Elevate-Möglichkeit einbauen (verb "runas" statt "open").
4. Auf Rechnern mit Terminal Services (also auch Fast User Switching) könntest Du Probleme haben. Gib dem Semaphor vllt. besser mal nen "Local\"-Prefix.

Björn Ole 6. Aug 2011 17:28

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Danke für die Vorschläge, ich arbeite mich mal durch:

Zitat:

Zitat von chaosben (Beitrag 1115063)
Der Restarter ist ein class helper für TApplication und initialisiert sich selbst über die init-Routine der Unit.

Vom Programmcode her schöner find ich das nicht, allerdings hast du Recht, ist besser wenn man die Unit auch mit älteren Delphi Versionen benutzen kann. Ich hab es mal geändert.

Zitat:

Zitat von chaosben (Beitrag 1115063)
Das hätte den Vorteil, das sich der "Aufwand" auf das Einbinden der Unit beschränkt

Die GUID muss allerdings immer noch irgendwo gesetzt werden, das wär ja unschön wenn man dafür in die Unit rein muss. Daher wird das Initialize wohl bleiben müssen.

Zitat:

Zitat von CCRDude (Beitrag 1115064)
1. Warum "if ParamStr(1)=" und nicht "FindCmdLineSwitch()"?

Die Funktion kannte ich noch gar nicht, danke, hab sie eingebaut.

Zitat:

Zitat von CCRDude (Beitrag 1115064)
2. Wenn bereits eine Instanz läuft, solltest Du die Parameter an diese weitergeben. Z.B. per Named Pipe.

Gute Idee, allerdings kommen mir Named Pipes dafür etwas zu umfangreich vor. Ich hab sie noch nie benutzt, von dem was ich lese scheint es mir aber etwas Overkill zu sein. Warum übergebe ich an ShellExecute nicht einfach
Delphi-Quellcode:
CmdLine + ' restart'
. Einwände?

Zitat:

Zitat von CCRDude (Beitrag 1115064)
3. Für Vista und später solltest Du eine Elevate-Möglichkeit einbauen (verb "runas" statt "open").

Verstehe nicht genau, was du meinst. open reicht doch alle Rechte weiter. Mit runas springt bei mir z.B. beim Neustart die UAC an, was ja nicht immer von Nöten ist.

Zitat:

Zitat von CCRDude (Beitrag 1115064)
4. Auf Rechnern mit Terminal Services (also auch Fast User Switching) könntest Du Probleme haben. Gib dem Semaphor vllt. besser mal nen "Local\"-Prefix.

Ist erledigt.

Für weitere Ideen bin ich immer dankbar. Werde noch etwas testen und dann bald die aktualisierte Unit posten.

chaosben 6. Aug 2011 21:30

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Warum muss es zwingend eine GUID sein? Per default würde es imho auch der Pfad zu Programm tun. Und für den Fall, das das nicht eindeutig genug ist, kann man ja noch eine GUID setzen. Denk ich. :-)

DeddyH 7. Aug 2011 09:52

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Verzeiht mir meine Unwissenheit, aber wann ist es eigentlich notwendig, dass sich eine Anwendung selbst neu starten muss? :gruebel:

EWeiss 7. Aug 2011 10:09

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
KA was du da machst aber das geht mit 1 Zeile.
Delphi-Quellcode:
ShellExecute(0, 'open', PWideChar(ParamStr(0)), nil, nil, SW_SHOW)


Noch einfacher geht es eigentlich nicht.
Und das sollte ab D1 funktionieren :)

Zitat:

aber wann ist es eigentlich notwendig, dass sich eine Anwendung selbst neu starten muss
Zum Beispiel bei mir wenn man seiner Anwendung Skins verpaßt hat.

gruss

WM_CLOSE 7. Aug 2011 10:18

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Zum Beispiel dann, wenn die Anwendung (aus welchem Grund auch immer) Admin-Rechte braucht.
Oder der Einfachheit halber, um die Initialisierung beim Programmstart nochmal zu durchlaufen, ohne große Änderungen am Code.
Oder bei einer unerwarteten Exception, bei der man nicht genau weiß was gerade schiefgelaufen ist (z.B. DB-Verbindung kaputt).
Oder wenn es ein Update der mitgelieferten DLLs gemacht hat und diese dann "sauber" neu laden soll.

Die letzten 3 treten bei einem sauber programmierten Programm so gut wie gar nicht auf, bei bestehendem Code vielleicht schon.

Björn Ole 7. Aug 2011 16:59

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Zitat:

Zitat von chaosben (Beitrag 1115426)
Warum muss es zwingend eine GUID sein? Per default würde es imho auch der Pfad zu Programm tun.

Hmpf, hast Recht. Hab den GUID Parameter entfernt und folgenden Namen gegeben:
Delphi-Quellcode:
semaphoreName := 'Local\' + StringReplace(ParamStr(0), '\', '/', [rfReplaceAll]);
Allerdings ist ein Editieren der .dpr immer noch notwendig, da ja der Programmstart u.U. abgebrochen werden muss. Oder kann ich das irgendwie sauber aus dem initialization Teil der Unit heraus machen?

Zitat:

Zitat von EWeiss (Beitrag 1115476)
KA was du da machst aber das geht mit 1 Zeile.
Delphi-Quellcode:
ShellExecute(0, 'open', PWideChar(ParamStr(0)), nil, nil, SW_SHOW)

Ja, so funktioniert es auch, allerdings kannst du so nicht garantieren, dass nur höchstens eine Instanz deiner Anwendung läuft. Das ist bei mir leider notwendig.

EWeiss 7. Aug 2011 17:46

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Dann noch 3 zeilen dazu ;)

Delphi-Quellcode:
initialization
  hMutex := CreateMutex(nil, True, 'Meine Anwendung);
  if GetLastError = ERROR_ALREADY_EXISTS then
    Halt;

finalization
  if hMutex <> 0 then
    CloseHandle(hMutex)
Soll aber nicht heißen das deine Unit nicht das selbe macht.

gruss

Björn Ole 7. Aug 2011 17:52

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Mit deinen 3 Zeilen mehr geht aber dein
Delphi-Quellcode:
ShellExecute(0, 'open', PWideChar(ParamStr(0)), nil, nil, SW_SHOW);
nicht mehr, weil der Mutex noch belegt ist. ;)

EWeiss 7. Aug 2011 19:35

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Zitat:

Zitat von Björn Ole (Beitrag 1115534)
Mit deinen 3 Zeilen mehr geht aber dein
Delphi-Quellcode:
ShellExecute(0, 'open', PWideChar(ParamStr(0)), nil, nil, SW_SHOW);
nicht mehr, weil der Mutex noch belegt ist. ;)

NÖ geht immer bei mir ;)

Aber egal..

gruss

CCRDude 7. Aug 2011 21:31

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Zitat:

Zitat von Björn Ole (Beitrag 1115393)
Zitat:

Zitat von CCRDude (Beitrag 1115064)
2. Wenn bereits eine Instanz läuft, solltest Du die Parameter an diese weitergeben. Z.B. per Named Pipe.

Gute Idee, allerdings kommen mir Named Pipes dafür etwas zu umfangreich vor. Ich hab sie noch nie benutzt, von dem was ich lese scheint es mir aber etwas Overkill zu sein. Warum übergebe ich an ShellExecute nicht einfach
Delphi-Quellcode:
CmdLine + ' restart'
. Einwände?

Vielleicht habe ich auch die Funktionalität deines Codes falsch interpretiert :)
Das wäre notwendig, wenn es darum gehen würde, daß nur eine Instanz des Programmes laufen soll. Dann müssen spätere Instanzen das an die erste weiterreichen. Wie EWeiss später vorschlägt (allerdings auch ohne dieses Weiterreichen).

Zitat:

Zitat von Björn Ole (Beitrag 1115393)
Zitat:

Zitat von CCRDude (Beitrag 1115064)
3. Für Vista und später solltest Du eine Elevate-Möglichkeit einbauen (verb "runas" statt "open").

Verstehe nicht genau, was du meinst. open reicht doch alle Rechte weiter. Mit runas springt bei mir z.B. beim Neustart die UAC an, was ja nicht immer von Nöten ist.

Als Option - Programme laufen erstmal mit geringen Rechten, stelle aber evtl. möglich Administrationoptionen nach Elevation zur Verfügung (siehe WM_CLOSE).

Björn Ole 7. Aug 2011 22:38

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von EWeiss (Beitrag 1115544)
NÖ geht immer bei mir ;)

Da muss aber bei deinem Betriebssystem was ordentlich schief laufen. :shock:

Zitat:

Zitat von CCRDude (Beitrag 1115553)
Als Option - Programme laufen erstmal mit geringen Rechten, stelle aber evtl. möglich Administrationoptionen nach Elevation zur Verfügung (siehe WM_CLOSE).

Ah ok, ich glaub jetzt hab ich es verstanden. Im Anhang die neue Version, hat sich alles um einiges vereinfacht. Warum ich da vorher den Umweg über einen Thread gegangen bin... wer weiß :stupid:
Es besteht die Möglichkeit, dem Restart() einen Boolean mitzugeben, ob mit erhöhten Rechten gestartet werden soll. Programmparameter werden der neuen Instanz weitergegeben.

Folgendes hat sich bzgl. Benutzung geändert:

.dpr
Delphi-Quellcode:
if not Application.InitializeRestarter then
  Exit;
Code zum Neustarten
Delphi-Quellcode:
Application.Restart;
// oder
if not Application.Restart(bElevated) then
  ShowMessage('Fehler beim Neustarten: ' + SysErrorMessage(GetLastError));
Schade, dass ich den Eingangsposting nicht mehr ändern kann... :? Vor allem, da man die Unit jetzt auch mit älteren Versionen vor 2010 benutzen kann.

Florian Hämmerle 8. Aug 2011 00:51

AW: [Unit für Delphi >= 2010] Eigene Anwendung neu starten
 
Schreib einen Moderator an, damit er es in die Projekte-Sparte schiebt, dann kannst du dein erstes Posting immer updaten :)

Viele Grüße,
Florian


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