Удаление файлов используемых системой в Windows 98

Время от времени приходится сталкиваться с проблемой перемещения или удаления файлов, которые используются системой в настоящий момент, будь то исполняемый модуль или DLL. Если бы речь шла о Windows NT, то там все предельно просто и, можно сказать, красиво:

MoveFileEx(откуда, куда, MOVEFILE_DELAY_UNTIL_REBOOT)

И все! А хочешь удалить:

MoveFileEx(откуда, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)

Тоже хорошо.

Но беда в том, что эта функция не поддерживается в Windows 9X, ну и ладно сделаем её сами, благо Microsoft оставил нам такую возможность. В Windows 9X используется тот же принцип, что и в NT - на диске сохраняется информация о перемещаемых файлах, которая используется при загрузке системы. Разница в том, что NT использует для этой цели реестр:

(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations), 

А - файл WININIT.INI, если вы заглянете в каталог Windows, то скорей всего вы его там не найдете, вместо этого там лежит WININIT.BAK. Если мы заглянем в него, то увидим что-нибудь типа:

[rename]
C:\WINDOWS\SYSTEM\OLEAUT32.DLL=C:\WINDOWS\SYSTEM\OLEAUT32.001
NUL=C:\WINDOWS\SYSTEM\DDHELP.EXE
......

Здесь то, что осталось после инсталляции (у меня от DirectX SDK). Думаю, что детального описания не требуется, в отличие от MoveFileEx, здесь изменено направление - куда=откуда, а вместо NULL - NUL. Одно небольшое замечание: файл WININIT.INI используется в WININIT.EXE при запуске системы, оно не является приложением Win32 и стартует до загрузки защищенного режима диска, поэтому имена файлов (и пути разумеется) должны быть "короткими" (в формате 8.3). Ну а теперь посмотрим что мы с этого имеем. За основу возьмем код Джеффри Рихтера (Jeffry Richter) из Win32 Q&A, выкинем все "лишнее", дадим функции новое имя и снабдим нашими комментариями. Вот её реализация:

BOOL MoveFileOnReboot (LPCTSTR pszExisting, LPCTSTR pszNew) 
{
BOOL bResult = FALSE;

//буфер для нашей строки: куда=откуда
charszRenameLine[1024];
//длина строки; заодно заполним буфер
intcchRenameLine = wsprintfA(szRenameLine,
#ifdef UNICODE
"%ls=%ls\r\n",
#else
"%hs=%hs\r\n",
#endif
(pszNew == NULL) ? __TEXT("NUL") : pszNew, pszExisting); //если NULL,
//то строка будет NUL=откуда


//запомним как зовется секция в WININIT.INI,
char szRenameSec[] = "[Rename]\r\n";
//а длину сосчитаем по ходу
int cchRenameSec = sizeof(szRenameSec) - 1;

HANDLE hfile, hfilemap;
DWORD dwFileSize, dwRenameLinePos;
TCHAR szPathnameWinInit[_MAX_PATH];
//Сообразим полный путь к WININIT.INI
GetWindowsDirectory(szPathnameWinInit, _MAX_PATH);
lstrcat(szPathnameWinInit, __TEXT("\\WinInit.Ini"));
// Откроем или создадим WININIT.INI.
hfile = CreateFile(szPathnameWinInit,
GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if(hfile == INVALID_HANDLE_VALUE)
return bResult;
//Создаем hfilemap с учетом длины названия нашей секции
//и нашей строки

dwFileSize = GetFileSize(hfile, NULL);
hfilemap = CreateFileMapping(hfile, NULL, PAGE_READWRITE, 0,
dwFileSize + cchRenameLine + cchRenameSec, NULL);
if(hfilemap != NULL)
{
//Проецируем WININIT.INI в память.
LPSTR pszWinInit = (LPSTR)MapViewOfFile(hfilemap,
FILE_MAP_WRITE, 0, 0, 0);
if(pszWinInit != NULL)
{
// Ищем секцию [Rename]
LPSTR pszRenameSecInFile = strstr(pszWinInit, szRenameSec);
if(pszRenameSecInFile == NULL)
{
//Секции нет - будем добавлять.
dwFileSize += wsprintfA(&pszWinInit[dwFileSize], "%s",
szRenameSec);
//Позиция с которой вставлять
dwRenameLinePos = dwFileSize;
}
else
{
//Секция есть
PSTR pszFirstRenameLine = strchr(pszRenameSecInFile, '\n');
// Сдвинем содержимое файла на длину нашей строки,
// всегда будет добавлятся в начало списка

pszFirstRenameLine++;// 1-й символ новой строки
memmove(pszFirstRenameLine + cchRenameLine, pszFirstRenameLine,
pszWinInit + dwFileSize - pszFirstRenameLine);
// Позиция с которой будем вставлять
dwRenameLinePos = pszFirstRenameLine - pszWinInit;
}
// Вставляем строку
memcpy(&pszWinInit[dwRenameLinePos], szRenameLine,cchRenameLine);

UnmapViewOfFile(pszWinInit);
// Новая длина файла.
dwFileSize += cchRenameLine;
bResult = TRUE; //...все было просто здорово
}
CloseHandle(hfilemap);
}
//Добавляем EOF.
SetFilePointer(hfile, dwFileSize, NULL, FILE_BEGIN);
SetEndOfFile(hfile);
CloseHandle(hfile);
return bResult;
}

В качестве примера небольшой проект: при запуске приложения выводим диалог для ввода пароля и запускаем приложение, если введен неправильный пароль мы тихо удаляем программку. Запустите ее и не забудьте заглянуть в WININIT.INI. Конечно же это не защита, да и сам пример не претендует на изящность, главное - показать, что функция действительно работает. Здесь лишь принцип, и не сам принцип, а его идея. Суть в том, что мы усыпляем бдительность "не хорошего" юзера, даем ему поработать, а после всего уходим не прощаясь (помня о нанесенной обиде :)). Конечно, это подойдет не всегда и не всем, для того чтобы программе удалить себя, можно, к примеру, создав "на лету" batch с бесконечным циклом удаления exeшника. Единственное нормальное применение этому это, наверно, инсталлятор или функция обновления в самой программе.

 
« Предыдущая статья   Следующая статья »