Zitat von
hoika:
der Link unten zeigt c++-Quellcode zum Löschen der eigenen Exe.
Inwiefern das auch unter Win98 schon geht, weiss ich nicht.
Das geht nicht mal unter allen Windows NT Varianten
.
Zitat von
moelski:
hat das mit dem MoveFileEx() denn irgendwelche Nachteile
Ja, funktioniert nicht unter 98.
Aber 98 brauchen wir (leider) noch.
Das ist nicht dein Ernst, oder? Stimmt zwar, daß MoveFileEx unter Windows 9x nicht den MOVEFILE_DELAY_UNTIL_REBOOT Parameter implementiert, aber es ist ja nicht so als würde es unter Windows 9x keinen Mechanismus dafür geben:
Nämlich wininit.ini.
In einigen Zeilen Code hat man dann in Windeseile einen Wrapper geschrieben:
Delphi-Quellcode:
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;
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:
Delphi-Quellcode:
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.
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.
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 (
InnoSetup,
NSIS um nur zwei zu nennen, von komerziellen Lösungen mag ich gar nicht erst anfangen).
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.