讓你的MFC程序只能開一個例程

暫時有兩種方法實現,直接上代碼:

⑴將這段代碼放到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);  
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章