暫時有兩種方法實現,直接上代碼:
⑴將這段代碼放到app的InitInstance中:
// 脫殼部分,正式發佈需要打開
HANDLE mutex = NULL;
mutex=CreateMutex(0,false,"RegServer");//創建互斥體,創建進程希望立即擁有互斥體,則設爲TRUE,一個互斥體同時只能由一個線程擁有
if(GetLastError() == ERROR_ALREADY_EXISTS)
mutex=OpenMutex(MUTEX_ALL_ACCESS,false,"RegServer");//爲現有的一個已命名互斥體對象創建一個新句柄
LPWSTR *szArglist = NULL;
int nArgs;
szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);//szArglist就是保存參數的數組
//nArgs是數組中參數的個數
//數組的第一個元素表示進程的path,也就是szArglist[0],其他的元素依次是輸入參數。
if( NULL == szArglist )
{
CloseHandle(mutex);
return FALSE;
}
else
{
if(nArgs > 1)
LocalFree(szArglist);//釋放局部內存對象並使句柄失效
else
{
char exeFullPath[MAX_PATH];
char exePath[MAX_PATH];
char exeTmpFullPath[MAX_PATH];
char exeTmpName[MAX_PATH];
memset(exeFullPath,0,sizeof(exeFullPath));
memset(exePath,0,sizeof(exePath));
memset(exeTmpFullPath,0,sizeof(exeTmpFullPath));
strcpy(exeTmpName,"RegServer_bak.exe");
GetModuleFileNameA(NULL,exeFullPath,MAX_PATH);//得到進程的完全路徑
strcpy(exePath,exeFullPath);
size_t i;
for(i=strlen(exePath)-1; i>0 && exePath[i]!='\\'; i--);
exePath[i]='\0';//將文件名去掉
sprintf(exeTmpFullPath,"%s\\RegServer_bak.exe",exePath);//定位RegServer_bak.exe副本程序的絕對位置
/*WaitForSingleObject函數用來檢測hHandle事件的信號狀態,
在某一線程中調用該函數時,線程暫時掛起,如果在掛起的dwMilliseconds毫秒內,
線程所等待的對象變爲有信號狀態,則該函數立即返回;如果超時時間已經到達dwMilliseconds毫秒,
但hHandle所指向的對象還沒有變成有信號狀態,函數照樣返回。參數dwMilliseconds有兩個具有特殊意義的值:
0和INFINITE。若爲0,則該函數立即返回;若爲INFINITE,則線程一直被掛起,
直到hHandle所指向的對象變爲有信號狀態時爲止。*/
WaitForSingleObject(mutex,INFINITE);
int count=0;
CString strProcessName = exeTmpName;
//將字符串轉換爲小寫
strProcessName.MakeLower();
while(1)
{
//KILL Process begin
//創建進程快照(TH32CS_SNAPPROCESS表示創建所有進程的快照)
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);//獲取當前運行的所有進程信息
//PROCESSENTRY32進程快照的結構體
PROCESSENTRY32 pe;
//實例化後使用Process32First獲取第一個快照的進程前必做的初始化操作
pe.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnapShot,&pe);//得到運行的第一個進程
//如果句柄有效 則一直獲取下一個句柄循環下去
while (Process32Next(hSnapShot,&pe))
{
//pe.szExeFile獲取當前進程的可執行文件名稱
CString scTmp = pe.szExeFile;
//將可執行文件名稱所有英文字母修改爲小寫
scTmp.MakeLower();
//比較當前進程的可執行文件名稱和傳遞進來的文件名稱是否相同
//相同的話Compare返回0
if(!scTmp.Compare(strProcessName))
{
//從快照進程中獲取該進程的PID(即任務管理器中的PID)
DWORD dwProcessID = pe.th32ProcessID;
HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE,FALSE,dwProcessID);//OpenProcess 函數用來打開一個已存在的進程對象,並返回進程的句柄。
::TerminateProcess(hProcess,0);//TerminateProcess函數終止指定進程及其所有線程。
CloseHandle(hProcess);
break;
}
scTmp.ReleaseBuffer();
}
strProcessName.ReleaseBuffer();
//KILL Process end
if(!strcmp(exeFullPath,exeTmpFullPath))
break;
if(count > 300)
break;
CopyFileA(exeFullPath,exeTmpFullPath,FALSE);//複製原文件,製作拷貝
i=GetLastError();
if(i == 0)
{
break;
}
count++;
::Sleep(1000);
}
sprintf(exeTmpFullPath,"\"%s\\RegServer_bak.exe\" -2",exePath);
WinExec(exeTmpFullPath, SW_HIDE);//打開副本
LocalFree(szArglist);
ReleaseMutex(mutex);//釋放互斥體同時主線程被殺死,新的線程RegServer_bak.exe擁有互斥對象
CloseHandle(mutex);
return FALSE;
}
}
CloseHandle(mutex);
//end 生成可執行程序的副本 end//////
⑵ Windows是多進程操作系統,框架生成的應用程序可以多次運行,形成多個運行實例。但在有些情況下爲保證應用程序的安全運行,要求程序只能運行一個實例,比如程
序要使用只能被一個進程單獨使用的特殊硬件(例如調制解調器)時,必須限制程
序只運行一個實例。
這裏涉及兩個基本的問題,一是在程序的第二個實例啓動時,如何發現該程序已有
一個實例在運行,而是如何將第一個實例激活,而第二個實例退出。
對於第一個問題,可以通過給應用程序設置信號量,實例啓動時首先檢測該信號量,
如已存在,則說明程序已運行一個實例。
第二個問題的難點是獲取第一個實例的主窗對象指針或句柄,然後便可用
SetForegroundWindow來激活。雖然FindWindow函數能尋找正運行着的窗口,但該函
數要求指明所尋找窗口的標題或窗口類名,不是實現通用方法的途徑。
我們可以用Win32 SDK函數SetProp來給應用程序主窗設置一個特有的標記。
用GetDesktopWindow 可以獲取Windows系統主控窗口對象指針或句柄,所有應用程
序主窗都可看成該窗口的子窗口,即可用GetWindow函數來獲得它們的對象指針或句
柄。用Win32 SDK函數GetProp查找每一應用程序主窗是否包含有我們設置的特定標
記便可確定它是否我們要尋找的第一個實例主窗。使第二個實例退出很簡單,只要
讓其應用程序對象的InitInstance函數返回FALSE即可。此外,當主窗口退出時,應
用RemoveProp函數刪除我們爲其設置的標記。
下面的InitInstance、OnCreate和OnDestroy函數代碼將實現上述的操作:
BOOL CEllipseWndApp::InitInstance()
{
// 用應用程序名創建信號量
HANDLE hSem = CreateSemaphore(NULL, 1, 1, m_pszAppName);
// 信號量已存在?
// 信號量存在,則程序已有一個實例運行
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
// 關閉信號量句柄
CloseHandle(hSem);
// 尋找先前實例的主窗口
HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(),GW_CHILD);
while (::IsWindow(hWndPrevious))
{
// 檢查窗口是否有預設的標記?
// 有,則是我們尋找的主窗
if (::GetProp(hWndPrevious, m_pszAppName))
{
// 主窗口已最小化,則恢復其大小
if (::IsIconic(hWndPrevious))
::ShowWindow(hWndPrevious,SW_RESTORE);
// 將主窗激活
::SetForegroundWindow(hWndPrevious);
// 將主窗的對話框激活
::SetForegroundWindow(
::GetLastActivePopup(hWndPrevious));
// 退出本實例
return FALSE;
}
// 繼續尋找下一個窗口
hWndPrevious = ::GetWindow(hWndPrevious,GW_HWNDNEXT);
}
// 前一實例已存在,但找不到其主窗
// 可能出錯了
// 退出本實例
return FALSE;
}
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic();// Call this when linking to MFC statically
#endif
CEllipseWndDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
int CEllipseWndDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
// 設置尋找標記
::SetProp(m_hWnd, AfxGetApp()->m_pszAppName, (HANDLE)1);
return 0;
}
void CEllipseWndDlg::OnDestroy()
{
CDialog::OnDestroy();
// 刪除尋找標記
::RemoveProp(m_hWnd, AfxGetApp()->m_pszAppName);
}