![]() |
[gelöst] Henne Ei Problem - eigene EXE löschen
Moin !
Mein eigener Installer wird langsam rund doch ich bin gerade mal wieder auf ein Problem gestoßen ... Und zwar habe ich im Anwendungsverzeichnis meinen Uninstaller (Uninstall.exe) liegen. Der löscht auch alles was ich ihm aufgetragen habe, aber er kann sich nicht selber löschen - der Zugriff wird in dem Fall verweigert. Kann mir jemand einen Tip geben wie ich das nun hinbekomme das ich den Uninstaller auch mit löschen kann? |
Re: Henne Ei Problem - eigene EXE löschen
Das Thema gab es hier shcon öfter mal. Meistens kam dabei heraus, dass es am einfachsten ist, eine temporäre BAT-datei zu erstellen, die zuerst das Programm löscht und anschließend sich selbst - Batchdateien können das nämlich im Gegensatz zu kompilierten Programmen.
|
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Zitat:
Oder direkt die BAT starten ? |
Re: Henne Ei Problem - eigene EXE löschen
hallo Moelski,
hatten wir das nicht schonmal? sich selbst löschen kann die EXE nicht, das ginge nur über eine Batchdatei:
Delphi-Quellcode:
natürlich mit den richtigen Laufwerken/Verzeichnissen garniert.
Uninstaler
del Uninstaler.exe Gruß K-H ich bin nicht schnell genug!!! |
Re: Henne Ei Problem - eigene EXE löschen
Es gibt auch eine WinAPI-Funktion, mit der man Dateien zum Löschen markieren kann. Diese werden dann beim Herunterfahren (oder Neustarten) gelöscht.
|
Re: Henne Ei Problem - eigene EXE löschen
versuch mal
1. Programm starten 2. Alte Datei umbenennen 3. Neue Datei erstellen 4. Programm beenden 5. Neues Programm starten 6. Umbenannte Datei löschen |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Zitat:
|
Re: Henne Ei Problem - eigene EXE löschen
Achso, meinte damit, dass beim nächsten Programmstart die Datei gelöscht wird, hab den Anfangstext nicht richtig gelesen, dacht es geht um ne Live Update Funktion...
Und wenn du die Datei nicht nur umbenennst, sondern verschiebst? Was muss denn sein damit man eine exe nicht löschen kann, exklusiver Zugriff auf die Datei? Wer hat den Zugriff und kann man den exklusiven Zugriff aufheben? |
Re: Henne Ei Problem - eigene EXE löschen
Wenn der Uninstaller in C:\Programme\MyApp\uninstall.exe wohnt, könnte er sich ins Temp-Verzeichnis kopieren am Ende der Deinstallation, die Kopie ausführen und den Pfad des Original-Uninstallers mitgeben (ParamStr(1)). Die Kopie wartet, bis das Original beendet ist, löscht es dann (und das Verzeichnis MyApp) und legt für sich selbst dann mit MoveFileEx() das Löschen beim Neustart fest.
Ist auch nicht 1A, aber schöner als frickeleien mit Batch-Dateien, wie ich finde. |
Re: Henne Ei Problem - eigene EXE löschen
Probiers mal mit ...
Delphi-Quellcode:
function DeleteSelf(): Boolean;
var F : File of Byte; FP, Params: String; const B : String = ':loop'#13#10+ 'del /F "%s"'#13#10+ 'if EXIST "%s" goto loop'#13#10+ 'del /F %s'; begin Result := False; FP := ExtractFilePath( ParamStr(0) ); {$i-} AssignFile( F, FP + '_.bat' ); Rewrite( F ); Params := Format( B, [ParamStr(0), ParamStr(0), FP + '_.bat'] ); BlockWrite( F, Params[1], Length(Params) ); CloseFile( F ); {$i+} if not( IOResult = ERROR_SUCCESS ) then Exit; Result := ShellExecute( 0, 'open', pChar( FP + '_.bat' ), nil, nil, 0 ) > 32; end; |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Das geht schon ganz gut. Aber hat immer noch einen Haken ... Das Verzeichnis bleibt stehen. Ich habe mal versucht das hier noch an die BAT anzuhängen:
Delphi-Quellcode:
aber das bringts nicht.
'cd ..' + #13#10+
'RD "' + ParamStr(0) + '"' + #13#10 + In Summe schaut das nun so:
Delphi-Quellcode:
Den Ping habe ich reingenommen damit die CPU Last im Rahmen bleibt.
BatText := ':loop' + #13#10+
'ping localhost' + #13#10+ 'del /F "%s"' + #13#10+ 'if EXIST "%s" goto loop' + #13#10+ 'del /F "%s"' + #13#10+ 'cd ..' + #13#10+ 'RD "' + ExtractFilePath(Application.ExeName) + '"'; Aber das RD zeigt keine Wirkung. EDIT: 'RD "' + ParamStr(0) + '"' War natürlich ein Fehler. Geht aber dennoch nicht. |
Re: Henne Ei Problem - eigene EXE löschen
Warum ParamStr(0) ?
Bei folgender Anwendung - test.exe, die sich in C: befindet, liefert ParamStr(0) folgendes zurück: C:\test.exe - RD aber hingegen löscht nur Ordner.. Da ist was schiefgelaufen ;) MfG |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Drum hab ich ja auch sofort mein Post korrigiert auf ExtractFilePath(Application.ExeName) :wink: Aber es geht auch damit ned. |
Re: Henne Ei Problem - eigene EXE löschen
Zitat:
|
Re: Henne Ei Problem - eigene EXE löschen
Ah jetzt verstehe ich das ...
Wenn ich die Batch lösche von der Platte, dann kann der Command Prozessor auch nicht mehr die nächsten Befehle lesen und gibt den Fehler aus "Die Batchdatei kann nicht gefunden werden.". Also müsste man die Batch im Programm Root ablegen (c:\programme z.B.) und von dort laufen lassen. Und erst ganz zum Schluss die Batch löschen ... |
Re: Henne Ei Problem - eigene EXE löschen
Wo diese genau liegt sollte egal sein. Hauptsache du hast dort Schreibrechte und deren Löschung ist der letzte Schritt
|
Re: Henne Ei Problem - eigene EXE löschen
Zitat:
Vielleicht die Batch-Datei in den Temp-Ordner... dort wird sie dann irgendwann auch gelöscht, wenn sie durch einen Fehler als Leiche zurück bleibt. |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Hmm das geht aber dennoch nicht. Das Verzeichnis bleibt:
Delphi-Quellcode:
Ich habe das Verzeichnis für die BAT mal hart kodiert.
function DeleteSelf(): Boolean;
var F : File of Byte; FP, Params : String; BatText : String; begin BatText := ':loop' + #13#10+ 'ping localhost' + #13#10+ 'del /F "%s"' + #13#10+ 'if EXIST "%s" goto loop' + #13#10+ 'RD "' + ExtractFilePath(ParamStr(0)) + '"' + #13#10 + 'del /F "%s"' ; Result := False; FP := 'C:\Program Files\'; //ExtractFilePath( ParamStr(0) ); {$i-} AssignFile( F, FP + '_.bat' ); Rewrite( F ); Params := Format( BatText, [ParamStr(0), ParamStr(0), FP + '_.bat'] ); BlockWrite( F, Params[1], Length(Params) ); CloseFile( F ); {$i+} if not( IOResult = ERROR_SUCCESS ) then Exit; Result := ShellExecute( 0, 'open', pChar( FP + '_.bat' ), nil, nil, 1 ) > 32; end; Aber das Verzeichnis bleibt bestehen wenn ich es aus dem Programm raus starte. Starte ich die Batch alleine, geht es sauber. Merkwürdig ... |
Re: Henne Ei Problem - eigene EXE löschen
Also der andere hier vorgeschlagene Weg würde ich bevorzugen... Also den Uninstaller ins Temp-Verzeichnis kopieren und mit zu löschendem Pfad als Parameter aufrufen. Du hättest wesentlich mehr Kontrolle über den Löschvorgang.
Den Uninstaller im Temp-Verzeichnis dann mit DeleteSelf löschen... wenn das schief geht, liegen die "Reste" am richtigen Ort. |
Re: Henne Ei Problem - eigene EXE löschen
Hmm.. hat das mit dem MoveFileEx() denn irgendwelche Nachteile oder so? Wenn ich das mit der .bat sehe da bekomme ich fast - entschuldigung - das Grauen...
Und was mir noch einfällt, auf keinen Fall den Programm-Ordner einfach so löschen. Man sollte erst vergleichen, ob Dateien im Programmverzeichnis liegen, die der Benutzer nachträglich angelegt hat (nicht von dem Installer installiert). Da habe ich mir mit einer eigenen Deinstall-Routine selbst schon mal stark ins Knie geschossen... |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Delphi-Quellcode:
Ja, funktioniert nicht unter 98.
hat das mit dem MoveFileEx() denn irgendwelche Nachteile
Aber 98 brauchen wir (leider) noch. Zitat:
Zitat:
Aber ich gebe dir Recht. So eine Überprüfung könnte noch Sinn machen. Aber nicht bevor ich dieses Problem im Griff habe .. |
Re: Henne Ei Problem - eigene EXE löschen
Zitat:
Man bekommt dann immer die nette Meldung "Es konnte nicht alles gelöscht werden", womit der Ordner gemeint war, der nicht leer geworden ist. |
Re: Henne Ei Problem - eigene EXE löschen
Alles klar, das mit Win98 ist ein Argument. Bin ich froh, damit nichts mehr am Hut haben zu müssen.
Und weil Win98 eh lumpig ist, kann man dann auch den Uninstaller einfach im Temp-Ordner rumliegen lassen.. |
Re: Henne Ei Problem - eigene EXE löschen
Hallo,
der Link unten zeigt c++-Quellcode zum Löschen der eigenen Exe. Inwiefern das auch unter Win98 schon geht, weiss ich nicht. ![]() Heiko |
Re: Henne Ei Problem - eigene EXE löschen
Zitat:
Zitat:
![]() In einigen Zeilen Code hat man dann in Windeseile einen Wrapper geschrieben:
Delphi-Quellcode:
Wenn man dann noch das Verzeichnis entfernen will, bietet sich der bereits oft zitierte Trick mit der temporären Datei an. Sowas könnte dann z.B. so aussehen:
function GetWindowsFolder: string;
const MAX_PATH_NTFS = 32767; var CharArray : array[0..MAX_PATH_NTFS] of char; begin FillChar(CharArray, SizeOf(CharArray), 0); if GetWindowsDirectory(CharArray, MAX_PATH_NTFS) > 0 then Result := IncludeTrailingPathDelimiter(CharArray) else Result := ''; end; function IsWindowsNT : boolean; inline; begin Result := GetVersion and $80000000 = 0; end; function GetShortFilename(Filename: string) : string; var ShortFilename : array[0..MAX_PATH] of char ; begin if GetShortPathName(PChar(Filename), @ShortFilename, MAX_PATH) > 0 then Result := ShortFilename else Result := Filename; end; function DeleteFileOnReboot(Filename : string) : boolean; begin if IsWindowsNT then Result := MoveFileEx(PChar(ParamStr(0)), nil, MOVEFILE_DELAY_UNTIL_REBOOT) { FIXME: Prinzipiell können mehrere NUL Werte existieren. WritePrivateProfileString würde dann den jeweils ersten immer wieder überschreiben. Entsprechend wäre der 100% saubere Weg einen eigenen kleinen Parser für die wininit.ini zu bauen, der die rename Section sucht und die eigenen Einträge hinten dran hängt. } else Result := WritePrivateProfileString('rename', 'nul', PChar(GetShortFilename(Filename)), PChar(GetWindowsFolder + 'wininit.ini')); end;
Delphi-Quellcode:
Die Anwendung kopiert sich selbst in den Temp Ordner, ruft dann seine Kopie auf mit dem Originalnamen als Parameter. Danach versucht die Kopie das Original zu löschen. Sobald das geschehen ist, fügt es sich selbst zum Löschen bei Reboot hinzu. Prinzipiell kann man dann sobald die Original Datei gelöscht ist, auch das Verzeichnis entfernen indem man den Code in DeleteMyOldSelfAndMe an der Kommentarstelle erweitert.
program DeleteMe;
{$APPTYPE CONSOLE} uses SysUtils, windows; function GetWindowsFolder: string; const MAX_PATH_NTFS = 32767; var CharArray : array[0..MAX_PATH_NTFS] of char; begin FillChar(CharArray, SizeOf(CharArray), 0); if GetWindowsDirectory(CharArray, MAX_PATH_NTFS) > 0 then Result := IncludeTrailingPathDelimiter(CharArray) else Result := ''; end; function GetTempFolder : string; const MAX_PATH_NTFS = 32767; var CharArray : array[0..MAX_PATH_NTFS] of char; begin FillChar(CharArray, SizeOf(CharArray), 0); if GetTempPath(MAX_PATH_NTFS, @CharArray) > 0 then Result := IncludeTrailingPathDelimiter(CharArray) else Result := ''; end; function GetTemporaryFilename : string; var CharArray : array[0..MAX_PATH] of char; begin FillChar(CharArray, SizeOf(CharArray), 0); if GetTempFileName(PChar(GetTempFolder), 'Del', 0, @CharArray) <> 0 then Result := CharArray else Result := ''; end; function GetShortFilename(Filename: string) : string; var ShortFilename : array[0..MAX_PATH] of char ; begin if GetShortPathName(PChar(Filename), @ShortFilename, MAX_PATH) > 0 then Result := ShortFilename else Result := Filename; end; function IsWindowsNT : boolean; inline; begin Result := GetVersion and $80000000 = 0; end; function DeleteFileOnReboot(Filename : string) : boolean; begin if IsWindowsNT then Result := MoveFileEx(PChar(ParamStr(0)), nil, MOVEFILE_DELAY_UNTIL_REBOOT) { FIXME: Prinzipiell können mehrere NUL Werte existieren. WritePrivateProfileString würde dann den jeweils ersten immer wieder überschreiben. Entsprechend wäre der 100% saubere Weg einen eigenen kleinen Parser für die wininit.ini zu bauen, der die rename Section sucht und die eigenen Einträge hinten dran hängt. } else Result := WritePrivateProfileString('rename', 'nul', PChar(GetShortFilename(Filename)), PChar(GetWindowsFolder + 'wininit.ini')); end; procedure ExecuteFile(Filename, Parameters : string); var StartupInformation : TStartupInfo; ProcessInformation : TProcessInformation; begin writeln('Temporaere Datei erstellt: ', Filename); FillChar(StartupInformation, SizeOf(TStartupInfo), 0); FillChar(ProcessInformation, SizeOf(TProcessInformation), 0); StartupInformation.cb := SizeOf(TStartupInfo); CreateProcess(nil, PChar('"' + Filename + '" ' + Parameters), nil, nil, false, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInformation, ProcessInformation); end; procedure CreateAndRunCopyOfMyself(); var RandomFilename : string; begin RandomFileName := GetTemporaryFilename; if RandomFilename <> '' then if CopyFile(PChar(ParamStr(0)), PChar(RandomFilename), false) then ExecuteFile(RandomFilename, '"' + ParamStr(0) + '"') else DeleteFile(PChar(RandomFilename)); end; procedure DeleteMyOldSelfAndMe(); begin if FileExists(ParamStr(1)) then repeat writeln('Versuche alte Datei zu entfernen ...'); Sleep(1000); until DeleteFile(PChar(ParamStr(1))); // Prinzipiell könnte man jetzt auch das Verzeichnis entfernen nur so nebenbei ... if DeleteFileOnReboot(ParamStr(0)) then writeln('Alte Datei entfernt und mich selbst fuer Entfernung bei Reboot eingetragen.') else writeln('Alte Datei entfernt, aber ich selbst bin noch da: ', ParamStr(0)); end; begin if ExtractFileExt(ParamStr(0)) = '.tmp' then DeleteMyOldSelfAndMe() else CreateAndRunCopyOfMyself; Writeln('Enter um die Anwendung zu beenden'); Readln; end. Prinzipiell könnte man auch Code in eine fremde Anwendung injezieren um die eigene Anwendung zu löschen. Das käme komplett ohne Temporäre Dateien oder ähnliches aus. Allerdings ist es je nach System relativ schwer zu implementieren. Davon abgesehen kreischen die ganzen HIPS und Behaviour Blocker immer gleich rum, wenn man Code Injection durchführt. Weiß nicht ob das entsprechend für Dich eine Option wäre. Achja: Ich behaupte übrigens, daß es keine Installationsaufgabe gibt, die man nicht mit einem der gängigen Installer Tools lösen kann. Entsprechend find ich es sinnbefreit einen womöglich dritt- oder viertklassigen Installer selbst zu bauen, wenns teilweise extrem leistungsfähige Open Source Projekte gibt ( ![]() ![]() Zu guter letzt weise ich darauf hin, daß ich der Übersicht halber nur rudimentäres Fehlerhandling implementiert hab. Bevor man den Code benutzt, sollte man ihn um entsprechendes Fehlerhandling erweitern. |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Danke für den Code. Werde ich doch gleich mal testen. Zitat:
Wir hatten zu Beginn Innosetup und danach AKInstallerMSI. Und du hast vollkommen Recht das wir damit alles lösen können. OT ein Wir wollten aber einen Installer der genau auf unsere Bedürfnisse zugeschnitten ist. Nämlich: - Installation / Update über Web - Installation / Update über Resourcen ZIP - Installation direkt auf USB Stick (bedarf ein paar extra Schritte) - sehr einfaches Handling Ich denke das unser Installer genau das erfüllt :) Und wir haben einen Installer wo wir die Quellcodes von Anfang an kennen und nach unseren Vorstellungen anpassen können. Mag sein das diese Aufgaben mit anderen Installern ebenfalls lösbar sind, aber nun ist er eh bald fertig und wie gesagt er macht genau das was wir brauchen. Nicht mehr und nicht weniger. OT aus |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
So code mal getestet und funzt soweit ganz gut. Aber das Löschen des Verzeichnisses bereitet noch ein Problem. Wenn ich die Anwendung absolut starte (aus dem Verzeichnis heraus oder per Link), dann wird das Verzeichnis nicht gelöscht. Starte ich die Anwendung relativ also z.B. "Neuer Ordner\Project1.exe" dann klappt auch das Löschen des Verzeichnisses. Finde ich etwas merkwürdig, denn erst wird das Verzeichnis geleert und dann versucht ja die zweite Instanz das Verzeichnis zu entfernen. Mir ist nicht ganz klar warum das in die Hose geht. :gruebel: |
Re: Henne Ei Problem - eigene EXE löschen
Vielleicht vorm Ordner löschen auch noch etwas warten (auch wenn das im Vergleich zur funktionierenden Methode unlogisch wäre) und den Fehler beim Ornder löschen genauer auswerten (beschreiben)... wenn es denn einen gibt.
|
Re: Henne Ei Problem - eigene EXE löschen
Moin !
"Warten" habe ich auch schon probiert. Bringt aber nichts. Muss mal sehen ob ich das Problem näher eingrenzen kann. |
Re: Henne Ei Problem - eigene EXE löschen
Hallo,
vielleicht solltest du vor dem Löschen per SetCurrentDirectory mal aus deinem Exe-Verzeichnis rausgehen (z.B. ins Temp). Heiko |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Zitat:
Ok so geht es nun. Werde nun mal auf verschiedenen Systemen testen. |
Re: Henne Ei Problem - eigene EXE löschen
Hallo,
ich will einen Keks ;) Du kannst ja zum Schluss die fertige Lösung posten. Heiko |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
Ja das kann ich gerne machen. Will aber erst noch ein bisserl Testen. |
Re: Henne Ei Problem - eigene EXE löschen
Moin !
So hier mal die Lösung in komplett... Ich nutze die DSiWin32 und habe Teile aus dem Orginalcode 0xF30FC7 ersetzt. -> ![]() An dieser Stelle nochmal herzlichen Dank an 0xF30FC7 und hoika für die entsprechenden Infos. :thumb: Evtl. wäre das ja was für die Codelib?
Delphi-Quellcode:
program DeleteMe;
{$APPTYPE CONSOLE} uses SysUtils, windows, DSiWin32 in '..\DSiWin32.pas'; function GetShortFilename(Filename: string) : string; var ShortFilename : array[0..MAX_PATH] of char ; begin if GetShortPathName(PChar(Filename), @ShortFilename, MAX_PATH) > 0 then Result := ShortFilename else Result := Filename; end; function DeleteFileOnReboot(Filename : string) : boolean; begin if DSiIsWinNT then Result := MoveFileEx(PChar(ParamStr(0)), nil, MOVEFILE_DELAY_UNTIL_REBOOT) { FIXME: Prinzipiell können mehrere NUL Werte existieren. WritePrivateProfileString würde dann den jeweils ersten immer wieder überschreiben. Entsprechend wäre der 100% saubere Weg einen eigenen kleinen Parser für die wininit.ini zu bauen, der die rename Section sucht und die eigenen Einträge hinten dran hängt. } else Result := WritePrivateProfileString('rename', 'nul', PChar(GetShortFilename(Filename)), PChar(DSiGetWindowsFolder + '\wininit.ini')); end; procedure ExecuteFile(Filename, Parameters : string); var StartupInformation : TStartupInfo; ProcessInformation : TProcessInformation; begin writeln('Temporaere Datei erstellt: ', Filename); FillChar(StartupInformation, SizeOf(TStartupInfo), 0); FillChar(ProcessInformation, SizeOf(TProcessInformation), 0); StartupInformation.cb := SizeOf(TStartupInfo); CreateProcess(nil, PChar('"' + Filename + '" ' + Parameters), nil, nil, false, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInformation, ProcessInformation); end; procedure CreateAndRunCopyOfMyself(); var RandomFilename : string; begin RandomFileName := DSiGetTempFileName('LV_', DSiGetTempPath); WriteLn('RandomFileName : ' + RandomFileName); if RandomFilename <> '' then if CopyFile(PChar(ParamStr(0)), PChar(RandomFilename), false) then ExecuteFile(RandomFilename, '"' + ParamStr(0) + '"') else DeleteFile(PChar(RandomFilename)); end; procedure DeleteMyOldSelfAndMe(); begin if FileExists(ParamStr(1)) then repeat Sleep(1000); writeln('Versuche alte Datei zu entfernen ...'); WriteLn(ParamStr(1)); until DeleteFile(PChar(ParamStr(1))); WriteLn('lösche ... ' + ExtractFilePath(ParamStr(1))); DSiRemoveFolder(ExtractFilePath(ParamStr(1))); if DeleteFileOnReboot(ParamStr(0)) then writeln('Alte Datei entfernt und mich selbst fuer Entfernung bei Reboot eingetragen.') else writeln('Alte Datei entfernt, aber ich selbst bin noch da: ', ParamStr(0)); end; begin WriteLn('Starte ...'); WriteLn(ParamStr(0)); WriteLn(ParamStr(1)); if LowerCase(ExtractFileExt(ParamStr(0))) = '.tmp' then begin WriteLn('-> Temp Version gestartet'); SetCurrentDirectory(PChar(DSiGetTempPath)); DeleteMyOldSelfAndMe(); end else begin WriteLn('-> Orginal Version gestartet'); CreateAndRunCopyOfMyself; end; Writeln('Enter um die Anwendung zu beenden'); Readln; end. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:23 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-2025 by Thomas Breitkreuz