::Главная страница :: С++/Си :: Статьи :: 1 вариант : 2 вариант

Как сделать так, чтобы программа сама себя могла стереть, т.е. свой *.exe файл

1 вариант

Удалить программу в тот момент, когда она запущена, не представляется возможным (во всяком случае такая возможность мне не знакома), остается удаление после завершения ее выполнения. Идея следующая: при выходе из программы создать BAT-файл, который ждет до тех пор, пока файл можно будет удалить (программа завершит работу), удаляет файл программы и себя, и запустить его:

 
void MyDlg::OnDestroy()
{
        CDialog::OnDestroy();

        const char *AppName=AfxGetApp()->m_pszExeName;
        FILE *f=fopen("selfdel.bat","w+");
        fprintf(f,
                ":dc\n"
                "del %s.exe\n"
                "if exist %s.exe goto dc\n"
                "del selfdel.bat",AppName,AppName);
        fclose(f);
        WinExec("selfdel.bat",FALSE);
}


Преимущества:
-файл удаляется сразу в тот момент, когда это становится возможно

Недостатки:
-если запустить два экземпляра приложения, то после завершения работы первого мы получаем цикл активного ожидания до тех пор пока не завершится второй экземпляр (это незаметно в W95/98, но в NT в окне Task Manager можно заметить полную загрузку процессора). Также пользователь все это время будет удивляться наличию невесть откуда взявшегося файла sefdel.bat

Майкрософт же предлагает свой способ решения проблемы, причем его реализация отличается для WinNT и Win95/98. Удаление (переименование, замещение, и т.д.) файла происходит во время следующей перезагрузки системы.

Win95/98: В процессе перезагрузки системы запускается утилита wininit.exe, которая осуществляет заданные действия над файлами, указанные в секции [rename] файла wininit.ini. При этом т.к. wininit.exe запускается еще до того как запущена система поддержки длинных имен файлов, все имена должны быть указаны в формате DOS (8.3).

Последовательность действий для удаления или переименования файла:
1.Проверить наличие файла WININIT.INI в директории Windows
2.Если WININIT.INI существует, открываем его и добавляем новые
строки в секцию [rename]. Если файла нет, создаем его и секцию [rename] в нем. 3.Добавляем строки следующего формата в секцию [rename]:
DestinationFileName=SourceFileName
Оба пути DestinationFileName и SourceFileName не должны содержать длинных имен. Приемник и источник должны находится на одном диске. Для удаления файла вместо DestinationFileName использовать NUL.

WinNT:
Здесь все сделано по-человечески. Для удаления файла следует использовать функцию MoveFileEx():
MoveFileEx(szSrcFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
где szSrcFile - имя файла или директории
Преимущества:
-"Лицензированный" метод Майкрософт
Недостатки:
-Чрезмерно утяжеленная процедура редактирования wininit.ini, проблемы при работе с длинными именами Win95/98,
-Удаление происходит только в момент перезагрузки. - Bad Sector

2 вариант

Программа не может удалить свой exe-файл, пока она работает. Это фундаментальное правило при работе под Windows. Поэтому всё, что остаётся - это поручить удаление другому процессу перед тем как завершить свой.

Самый простой вариант - создать на лету и запустить bat-файл, который дождётся завершения нашего процесса, а затем удалит его exe-файл. Более сложные варианты подразумевают создание в чужом процессе (например, в Task Manager) рабочего потока, который опять же дождётся завершения нашего процесса и убьёт файл.

Вот пример функции, которая создаёт bat-файл и запускает его, чтобы убить наш exe-файл. Лучше всего вызывать её непосредственно перед завершением нашего процесса.

 
void DelSelf()
{
 // Получаем свой путь
 char szExePath[MAX_PATH];
 GetModuleFileName(NULL, szExePath, MAX_PATH);

 // Создаём bat-файл
 static char szBat[] =
  ":Loop\r\n"
  "del %1\r\n"
  "if exist %1 goto Loop\r\n"
  "del %0";

 HANDLE hFile = CreateFile("__delself.bat", GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, 0);
 DWORD temp;
 WriteFile(hFile, (LPVOID)szBat, strlen(szBat), &temp, NULL);
 CloseHandle(hFile);

 // Запускаем его
 STARTUPINFO si;
 ZeroMemory(&si, sizeof(si));
 si.cb = sizeof(si);
 si.wShowWindow = SW_HIDE;
 si.dwFlags = STARTF_USESHOWWINDOW;

 PROCESS_INFORMATION pi;

 char szCommand[MAX_PATH+15] = "__delself.bat ";
 strcat(szCommand, szExePath);
 CreateProcess(NULL, szCommand, NULL, NULL, FALSE, DETACHED_PROCESS, NULL,
NULL, &si, &pi);

 return;
}


Замечу, что это только пример, который можно улучшать в различных направлениях. Можно, скажем, получать имя bat-файла через GetTempFileName, чтобы гарантировать его уникальность. Или понизить приоритет создаваемого из bat-файла процесса до минимума, чтобы он кушал поменьше ресурсов в процессе циклической проверки существования exe-файла. - Александр Шаргин (rudankort@mail.ru)

Тематические ссылки
Ваша ссылка Ваша ссылка

Обмен кнопками, ведение статистики, реклама.