典型場景:解安裝程式、一次性執行工具、安全敏感組件。使用者雙擊執行檔,程式完成任務後自動從磁碟消失,過程中主程式仍在記憶體中運行——這種「幽靈模式」常讓初學者驚呼「中毒了!」
Windows 在載入 EXE 時會以獨佔共享模式開啟檔案,導致:
DeleteFile 自身MoveFile 自身CreateFile 開啟自身核心策略:
%TEMP% 目錄(Clone)FILE_FLAG_DELETE_ON_CLOSE 開啟 Clone,承諾「所有句柄關閉時自動刪除」WaitForSingleObject 等待原始進程結束DeleteFile 刪除原始 EXEDELETE_ON_CLOSE)// 原始進程分支
if (__argc == 1) {
TCHAR szOrig[MAX_PATH], szClone[MAX_PATH];
GetModuleFileName(NULL, szOrig, MAX_PATH); // 取得自身完整路徑
GetTempPath(MAX_PATH, szClone); // 取得 %TEMP%
GetTempFileName(szClone, _T("Del"), 0, szClone); // 產生唯一暫存檔名
CopyFile(szOrig, szClone, FALSE); // 複製自身
// 關鍵:以 DELETE_ON_CLOSE 開啟 Clone
HANDLE hClone = CreateFile(szClone, 0, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
// 取得原始進程同步句柄
HANDLE hProc = OpenProcess(SYNCHRONIZE, TRUE, GetCurrentProcessId());
// 命令列:Clone路徑 PID 原始路徑
TCHAR cmd[512];
wsprintf(cmd, _T("%s %d \"%s\""), szClone, hProc, szOrig);
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
CloseHandle(hProc);
CloseHandle(hClone); // 關閉句柄,但檔案不刪除(Clone 進程仍持有)
return 0; // 原始進程退出
}
// Clone 進程分支
else {
HANDLE hOrig = (HANDLE)_ttoi(__targv[1]); // 還原 PID
WaitForSingleObject(hOrig, INFINITE); // 等待原始進程死亡
CloseHandle(hOrig);
DeleteFile(__targv[2]); // 刪除原始 EXE
// 此處可補充:刪除安裝目錄、註冊表等清理工作
// Clone 結束 → 系統自動刪除自身暫存檔
}
| 技術點 | 作用 | 注意事項 |
|---|---|---|
FILE_FLAG_DELETE_ON_CLOSE |
所有句柄關閉時自動刪除檔案 | 臨時檔案標配屬性,需在 CreateFile 指定 |
OpenProcess(SYNCHRONIZE, ...) |
取得可用於同步的進程句柄 | 需 SYNCHRONIZE 權限,NT 系有效 |
WaitForSingleObject(hProc, INFINITE) |
阻塞等待目標進程終止 | 避免原始進程尚未釋放檔案鎖就嘗試刪除 |
del %0 的 .bat,再 WinExec。簡單但會閃現命令提示字元。MoveFileEx(szOrig, NULL, MOVEFILE_DELAY_UNTIL_REBOOT),下次重啟才生效,不適合即時需求。GetModuleFileName 行為差異。「分身術」以用戶模式 API 完美解決「執行中無法刪除」的死結,代碼簡潔、無需驅動、相容性極佳。下次寫 Uninstaller 時,不妨直接採用這套經典模式——既專業又優雅。