再起動時に指定のファイルを削除する

インストーラーなどを作成していると現在実行中のファイル(例えば自分自身)を削除する必要が出てきます。このようなとき単に DeleteFile などのAPIを使用してもファイルが使用中なので削除できません。このような場合にはWindowsの再起動時に削除するファイルに登録します。

WindowsNTの場合にはAPI一つ( MoveFileEx )を呼び出すだけです。

::MoveFileEx(lpszFileName, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);

MOVEFILE_DELAY_UNTIL_REBOOT を指定することにより lpszFileName の削除は次にWindowsNTを再起動する時に行われます。

Windows95やWindows98の場合には MoveFileEx がサポートされていないので、WinInit.iniに書き込むことで登録します。WinInit.iniはWindowsディレクトリにあるので GetWindowsDirectory を使用してWindowsディレクトリを取得します。また、INIファイルへの書き込みには WritePrivateProfileString を使用します。

TCHAR szWinInitFile[MAX_PATH];
::GetWindowsDirectory(szWinInitFile, countof(szWinInitFile));
::lstrcat(szWinInitFile, "\\WinInit.ini");
::WritePrivateProfileString("Rename", "Nul", lpszFileName, szWinInitFile);

(98/11/17追記)
と、VCのヘルプには書かれているのですが、どうもうまくいきませんでした。よく考えると WritePrivateProfileString というAPIを使っていたのでは同じキー名で二つ以上の値を登録できないのでした。要するに、二つ以上のファイルを削除しようとしても後から登録したファイルしか削除されません。また、他のアプリケーションなどで既にNulに値が設定されていたりするとその値を上書きしてしまうことになります。そこで WritePrivateProfileSection というAPIを使用して次のように書き換えます。

TCHAR szWinInitFile[MAX_PATH];
::GetWindowsDirectory(szWinInitFile, countof(szWinInitFile));
::lstrcat(szWinInitFile, "\\WinInit.ini");
// 95/98でGetPrivateProfileSectionや
// WritePrivateProfileSectionを使用して読み書きできるサイズは32Kまで
TCHAR szBuffer[32768];
// まず最初にセクション全体をバッファーに読込む
::GetPrivateProfileSection("Rename", szBuffer, countof(szBuffer), szWinInitFile);
// セクションの最後を検索
LPTSTR lpsz = szBuffer;
while (*lpsz){
while (*lpsz++)
;
}
// Nul=[FileName]の形式でバッファーに追加
lstrcpy(lpsz, "Nul=");
lstrcat(lpsz, lpszFileName);
// 終端を書き込む
while (*lpsz++)
;
*lpsz = '\0';
// INIファイルへRenameセクションを書き込む
::WritePrivateProfileSection("Rename", szBuffer, szWinInitFile);

こうすることによりNulというキー名が複数あった場合にでもINIファイルへ書き込まれます。
(以上 98/11/17追記分)

これだけではまだWinInit.iniには書込まれていません。プログラムが実行中に再起動するとそのまま書込まれずに終了してしまい実際には削除されません。確実に書込むためには

::WritePrivateProfileString(NULL, NULL, NULL, szWinInitFile);

も追加しておきましょう

どちらの場合もNULL、Nulの部分でファイル名を指定してやればファイルを移動させることができます。その場合、移動先にファイルがあってはいけないので移動先のファイルを削除しておきます。


戻る