Einzelnen Beitrag anzeigen

Fridolin Walther

Registriert seit: 11. Mai 2008
Ort: Kühlungsborn
446 Beiträge
 
Delphi 2009 Professional
 
#25

Re: Henne Ei Problem - eigene EXE löschen

  Alt 3. Jun 2009, 03:22
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.
Fridolin Walther
  Mit Zitat antworten Zitat