轉(zhuǎn)自:
http://topic.csdn.net/t/20020917/11/1030014.html使應(yīng)用程序只能運(yùn)行一個(gè)實(shí)例
Windows是多進(jìn)程操作系統(tǒng),框架生成的應(yīng)用程序可以多次運(yùn)行,形成多個(gè)運(yùn)行實(shí)例。
但在有些情況下為保證應(yīng)用程序的安全運(yùn)行,要求程序只能運(yùn)行一個(gè)實(shí)例,比如程
序要使用只能被一個(gè)進(jìn)程單獨(dú)使用的特殊硬件(例如調(diào)制解調(diào)器)時(shí),必須限制程
序只運(yùn)行一個(gè)實(shí)例。
這里涉及兩個(gè)基本的問題,一是在程序的第二個(gè)實(shí)例啟動(dòng)時(shí),如何發(fā)現(xiàn)該程序已有
一個(gè)實(shí)例在運(yùn)行,而是如何將第一個(gè)實(shí)例激活,而第二個(gè)實(shí)例退出。
對于第一個(gè)問題,可以通過給應(yīng)用程序設(shè)置信號(hào)量,實(shí)例啟動(dòng)時(shí)首先檢測該信號(hào)量,
如已存在,則說明程序已運(yùn)行一個(gè)實(shí)例。
第二個(gè)問題的難點(diǎn)是獲取第一個(gè)實(shí)例的主窗對象指針或句柄,然后便可用
SetForegroundWindow來激活。雖然FindWindow函數(shù)能尋找正運(yùn)行著的窗口,但該函
數(shù)要求指明所尋找窗口的標(biāo)題或窗口類名,不是實(shí)現(xiàn)通用方法的途徑。
我們可以用Win32 SDK函數(shù)SetProp來給應(yīng)用程序主窗設(shè)置一個(gè)特有的標(biāo)記。
用GetDesktopWindow 可以獲取Windows系統(tǒng)主控窗口對象指針或句柄,所有應(yīng)用程
序主窗都可看成該窗口的子窗口,即可用GetWindow函數(shù)來獲得它們的對象指針或句
柄。用Win32 SDK函數(shù)GetProp查找每一應(yīng)用程序主窗是否包含有我們設(shè)置的特定標(biāo)
記便可確定它是否我們要尋找的第一個(gè)實(shí)例主窗。使第二個(gè)實(shí)例退出很簡單,只要
讓其應(yīng)用程序?qū)ο蟮腎nitInstance函數(shù)返回FALSE即可。此外,當(dāng)主窗口退出時(shí),應(yīng)
用RemoveProp函數(shù)刪除我們?yōu)槠湓O(shè)置的標(biāo)記。
下面的InitInstance、OnCreate和OnDestroy函數(shù)代碼將實(shí)現(xiàn)上述的操作:
BOOL CEllipseWndApp::InitInstance()
{
// 用應(yīng)用程序名創(chuàng)建信號(hào)量
HANDLE hSem = CreateSemaphore(NULL, 1, 1, m_pszExeName);
// 信號(hào)量已存在?
// 信號(hào)量存在,則程序已有一個(gè)實(shí)例運(yùn)行
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
// 關(guān)閉信號(hào)量句柄
CloseHandle(hSem);
// 尋找先前實(shí)例的主窗口
HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(),GW_CHILD);
while (::IsWindow(hWndPrevious))
{
// 檢查窗口是否有預(yù)設(shè)的標(biāo)記?
// 有,則是我們尋找的主窗
if (::GetProp(hWndPrevious, m_pszExeName))
{
// 主窗口已最小化,則恢復(fù)其大小
if (::IsIconic(hWndPrevious))
::ShowWindow(hWndPrevious,SW_RESTORE);
// 將主窗激活
::SetForegroundWindow(hWndPrevious);
// 將主窗的對話框激活
::SetForegroundWindow(
::GetLastActivePopup(hWndPrevious));
// 退出本實(shí)例
return FALSE;
}
// 繼續(xù)尋找下一個(gè)窗口
hWndPrevious = ::GetWindow(hWndPrevious,GW_HWNDNEXT);
}
// 前一實(shí)例已存在,但找不到其主窗
// 可能出錯(cuò)了
// 退出本實(shí)例
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;
// 設(shè)置尋找標(biāo)記
::SetProp(m_hWnd, AfxGetApp()->m_pszExeName, (HANDLE)1);
return 0;
}
void CEllipseWndDlg::OnDestroy()
{
CDialog::OnDestroy();
// 刪除尋找標(biāo)記
::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName);
}
對以上代碼的補(bǔ)充:
查看代碼和VC的幫助后,發(fā)現(xiàn)問題在于原文在創(chuàng)建信號(hào)量和設(shè)置尋找標(biāo)記時(shí)使用
的是 CWinApp 的成員變量 m_pszExeName,該成員變量其實(shí)是應(yīng)用程序執(zhí)行文件的名
稱去掉擴(kuò)展名后的部分,而不是應(yīng)用程序名。
真正的應(yīng)用程序名應(yīng)為成員變量 m_pszAppName,
于是將用到m_pszExeName的三處代碼均改為m_pszAppName,重新編譯執(zhí)行,情況消失。
最后再提供一個(gè)方法和一個(gè)信息:
另一種使應(yīng)用程序只能運(yùn)行一個(gè)實(shí)例的方法,只需在InitInstance()的最開始添
加下列語句即可:
HANDLE m_hMutex=CreateMutex(NULL,TRUE, m_pszAppName);
if(GetLastError()==ERROR_ALREADY_EXISTS) { return FALSE; }
但這種方法的不足之處是不能將已經(jīng)啟動(dòng)的實(shí)例激活。
在stingray公司整理開發(fā)的MFCFAQ軟件中也提供了一些方法。該軟件實(shí)際是一個(gè)
MFC使用技巧的大匯集,對使用MFC極有幫助,各位朋友不妨去stingray公司的主頁下
載。
posted on 2008-11-02 22:37
Sandy 閱讀(1202)
評論(3) 編輯 收藏 引用 所屬分類:
C++