最初是按照 FILE_FLAG_DELETE_ON_CLOSE的线索做的实验, 后来发现原始exe可以删除,但是克隆exe删不掉.
原因是进程在运行, 无法被删除.
后来在codeproject上看到释放.bat文件来删除主程序的方法. 这个方法实现, 思路明了. 实验的效果也不错.
/*****************************************************************************/ <2011_0206_2149><home><LostSpeed><程序自删除 V1.0.0.1> * 增加了DeleteMySelf()和支持的函数, 使程序自删除实现. * 建立了Console, Dialog, Sdi, Mdi工程, 分别做了测试, 自删除功能正常 * 主程序结束时, 在系统临时目录中建立了一个uuid.bat(名字是唯一的), 负责删除主程序, 用CreateProcess启动uuid.bat时, 隐藏了.bat的命令行窗口. 删除程序时的效果很棒, 看不到屏幕上有命令行窗口的闪烁. uuid.bat启动后, 循环检测主程序是否存在, 不停的删除主程序. 然后uuid.bat删除自己. * 如果收尾工作很繁杂, 可以另外做一个exe来做收尾工作(bat文件做的事还是有限), 把这个exe嵌到入主程序, 当程序退出时, 释放这个exe(没有窗口的console程序)到临时目录, 起名叫uuid.exe(保持文件名的唯一性), 然后建立,启动uuid.bat. uuid.bat启动这个收尾程序, 然后检测收尾程序是否结束. 如果结束, 删除收尾程序和uuid.bat /*****************************************************************************/
Console工程:
// DeleteMySelf.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <process.h> #include <malloc.h> #include "DeleteMySelf.h" #pragma comment(lib, "rpcrt4.lib")/**< for CoCreateGuid */ #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif / // The one and only application object #define MY_PATH_LEN_MAX 4096 VOID showCmdLine(int& argc, TCHAR** argv); BOOL IsNeedDeleteMySelf(int& argc, TCHAR** argv); INT DeleteMySelf(); void UseBatDeleteProcess(PCHAR temppath, PCHAR modulename); INT useCreateProcessHideCmdProg(PCHAR pcCmdProgName); BOOL GetSpecialFolder(UINT SpecialFolder, CString &SpecialFolderString); BOOL GetTempFolder(CString &strTempFolder); BOOL GetProgInfo(CString &strProgFolder, CString &strProgName); CString GenerateUniquePrefixName(); CWinApp theApp; using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { printf(">> _tmain/n"); int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } else { //程序主流程处理 CString strHello; strHello.LoadString(IDS_HELLO); cout << (LPCTSTR)strHello << endl; //程序收尾时, 处理自删除 showCmdLine(argc, argv); if(IsNeedDeleteMySelf(argc, argv)) { printf("需要自删除/n"); DeleteMySelf(); printf("<< _tmain/n"); return 0; } else printf("不需要自删除/n"); } printf("<< _tmain/n"); return nRetCode; } CString GenerateUniquePrefixName() { CString strUnique; GUID guid; HRESULT hRc = CoCreateGuid(&guid); if(S_OK != hRc) { strUnique.Format("%d", rand()); } else { BYTE * str; UuidToString((UUID*)&guid, &str); strUnique = str; RpcStringFree(&str); } return strUnique; } INT DeleteMySelf() { BOOL bRc = FALSE; DWORD dwRc = 0; DWORD dwProcessIdOld = 0; DWORD dwProcessIdNew = 0; CString strSystemFolder; CString strProgFolder; CString strProgName; CString strProgNewPathName; CString strProgOldPathName; CString csMsg; //把文件生成在临时文件夹好些 GetTempFolder(strSystemFolder); // if(!GetSpecialFolder(CSIDL_APPDATA, strSystemFolder)) // return S_FALSE; GetProgInfo(strProgFolder, strProgName); //C:/DOCUME~1/LOSTSP~1/LOCALS~1/Temp/6376e6f8-c94a-4c69-b1c4-aa749a5ec79a.bat strProgNewPathName = strSystemFolder + GenerateUniquePrefixName() + ".bat"; //D:/LsPrj/subjectResearch/程序自删除/src/mainprog/DeleteMySelf/Release/DeleteMySelf.exe strProgOldPathName = strProgFolder + strProgName; UseBatDeleteProcess((LPSTR)(LPCSTR)strProgNewPathName, (LPSTR)(LPCSTR)strProgOldPathName); return S_OK; } BOOL IsNeedDeleteMySelf(int& argc, TCHAR** argv) { int nagrcMin = 2; int nParamPos = 1; if(argc < nagrcMin) return FALSE; return (0 == strcmp("delself", argv[nParamPos])) ? TRUE : FALSE; } VOID showCmdLine(int& argc, TCHAR** argv) { INT n = 0; for(n = 0; n < argc; n++) { printf("argv[%d] = %s/n", n, argv[n]); } /** * @note run results argv[0] = DeleteMySelf.exe argv[1] = delself */ } BOOL GetProgInfo(CString &strProgFolder, CString &strProgName) { INT nSeparatorPos = 0; PCHAR pcTmp = NULL; CString strProgPathName; CString csMsg; pcTmp = strProgPathName.GetBuffer(MY_PATH_LEN_MAX); GetModuleFileName(NULL, pcTmp, MY_PATH_LEN_MAX); strProgPathName.ReleaseBuffer(); nSeparatorPos = strProgPathName.ReverseFind('//'); strProgFolder = strProgPathName.Left(nSeparatorPos + 1); strProgName = strProgPathName.Right(strProgPathName.GetLength() - nSeparatorPos - 1); csMsg = strProgFolder + "/n"; OutputDebugString(csMsg); csMsg = strProgName + "/n"; OutputDebugString(csMsg); /** * @note run results D:/LsPrj/subjectResearch/程序自删除/src/mainprog/DeleteMySelf/Debug/ DeleteMySelf.exe */ return S_OK; } BOOL GetSpecialFolder(UINT SpecialFolder, CString &SpecialFolderString) { HRESULT hr; // Find the Special Folder: // Allocate a pointer to an Item ID list LPITEMIDLIST pidl; // Get a pointer to an item ID list that // represents the path of a special folder hr = SHGetSpecialFolderLocation(NULL, SpecialFolder, &pidl); if(SUCCEEDED(hr)) { // Convert the item ID list's binary // representation into a file system path char szPath[_MAX_PATH]; if(SHGetPathFromIDList(pidl, szPath)) { // Allocate a pointer to an IMalloc interface LPMALLOC pMalloc; // Get the address of our task allocator's IMalloc interface hr = SHGetMalloc(&pMalloc); // Free the item ID list allocated by SHGetSpecialFolderLocation pMalloc->Free(pidl); // Free our task allocator pMalloc->Release(); // Work with the special folder's path (contained in szPath) SpecialFolderString = szPath; SpecialFolderString += "//"; return TRUE; } } return FALSE; } BOOL GetTempFolder(CString &strTempFolder) { PCHAR pcTmp = NULL; pcTmp = strTempFolder.GetBuffer(MY_PATH_LEN_MAX); GetTempPath(MY_PATH_LEN_MAX, pcTmp); strTempFolder.ReleaseBuffer(); return TRUE; } void UseBatDeleteProcess(PCHAR temppath, PCHAR modulename) { char pcBatContent[] = ":Repeat/r/n" "del /"%s/"/r/n" "if exist /"%s/" goto Repeat/r/n" "del /"%s/"" ; HANDLE hf ; hf = CreateFile(temppath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) ; if((hf) && (hf != INVALID_HANDLE_VALUE)) { DWORD dwLen = strlen(pcBatContent) + strlen(modulename) * 2 + strlen(temppath) + 1; PCHAR pcBat = new CHAR[dwLen]; ZeroMemory(pcBat, dwLen); sprintf(pcBat, pcBatContent, modulename, modulename, temppath) ; WriteFile(hf, pcBat, strlen(pcBat), &dwLen, NULL) ; CloseHandle(hf) ; delete pcBat; pcBat = NULL; //创建进程打开.bat, 打开的时候, 隐藏命令行窗口 useCreateProcessHideCmdProg(temppath); } } INT useCreateProcessHideCmdProg(PCHAR pcCmdProgName) { BOOL bRc = FALSE; PCHAR lpApplicationName = pcCmdProgName; /** 命令行参数开始必须带空格, 参数之间必须用空格隔开 */ PCHAR lpCommandLine = ""; STARTUPINFO sInfo; PROCESS_INFORMATION pInfo; BOOL bWaitApp = FALSE;/**< 开辟进程后, 是否要阻塞等待进程完成 */ /** * 隐藏标准控制台窗口 * 设置进程启动信息, STARTF_USESHOWWINDOW, SW_HIDE */ ZeroMemory(&sInfo, sizeof(STARTUPINFO)); sInfo.cb = sizeof(STARTUPINFO); sInfo.lpReserved = NULL; sInfo.lpReserved2 = NULL; sInfo.cbReserved2 = 0; sInfo.lpDesktop = NULL; sInfo.lpTitle = NULL; sInfo.dwFlags = STARTF_USESHOWWINDOW; sInfo.dwX = 0; sInfo.dwY = 0; sInfo.dwFillAttribute = 0; sInfo.wShowWindow = SW_HIDE; /** 如果需要指定的显示方式, 需要进一步设置si.xx*/ ZeroMemory(&pInfo, sizeof(PROCESS_INFORMATION)); bRc = ::CreateProcess( lpApplicationName, lpCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sInfo, &pInfo); ::WaitForSingleObject(pInfo.hProcess, bWaitApp ? INFINITE : 0); if(pInfo.hProcess) ::CloseHandle(pInfo.hProcess); if(pInfo.hThread) ::CloseHandle(pInfo.hThread); return S_OK; }
包含console, dialog, sdi, mdi自删除工程的下载点:
http://download.csdn.net/source/3012716
<2012_0617>
在Unicode字符集下编译不过, 找到UUID的新实现.
original url from : http://www.dotblogs.com.tw/larrynung/archive/2011/10/13/42376.aspx
{ _TUCHAR *guidStr = NULL; std::wstring strW; GUID *pguid = new GUID; CoCreateGuid(pguid); // Convert the GUID to a string UuidToString(pguid, (RPC_WSTR*)&guidStr); delete pguid; strW = std::wstring(guidStr); return strW.c_str(); }
修正后的GenerateUniquePrefixName()实现
{ CString strUnique; GUID guid; HRESULT hRc = CoCreateGuid(&guid); if(S_OK != hRc) { strUnique.Format(_T("%d"), rand()); } else { _TUCHAR * str = NULL; UuidToString(&guid, (RPC_WSTR*)&str); strUnique = str; RpcStringFree((RPC_WSTR*)str); } return strUnique; } 现在用CString感觉很别扭了~