如何控制一个程序只能有一个进程
EnumWindows的使用
BOOL EnumWindows( WNDENUMPROC lpEnumFunc, LPARAM lParam );该函数枚举所有屏幕上的顶层窗口,并将窗口句柄传送给应用程序定义的回调函数,其中lParam是传递给lpEnumFunc的参数。回调函数返回FALSE将停止枚举,否则EnumWindows函数继续到所有顶层窗口枚举完为止。例如获取所有的桌面窗口句柄,并将其值放到CListBox中:
进程统指应用程序的一个实例
两个问题:
1.在程序的第二个实例启动时,如何发现该程序已有 一个实例在运行
2.如何将第一个实例激活,而第二个实例退出
由于windows并没有给内核对象定义唯一的名字标准,而且不同的内核对象共用一个名字空间,可以借鉴定义一个有名字的内核对象来标识唯一(具体不做说明)
第二个问题的难点是获取第一个实例的主窗对象指针或句柄,然后便可用SetForegroundWindow来激活。虽然FindWindow函数能寻找正运行着的窗口,但该函数要求指明所寻找窗口的标题或窗口类名,不是实现通用方法的途径。我们可以用Win32 SDK函数SetProp来给应用程序主窗设置一个特有的标记。用GetDesktopWindow可以获取Windows系统主控窗口对象指针或句柄,所有应用程序 主窗都可看成该窗口的子窗口,即可用GetWindow函数来获得它们的对象指针或句 柄。用Win32 SDK函数GetProp查找每一应用程序主窗是否包含有我们设置的特定 标记便可确定它是否我们要寻找的第一个实例主窗。使第二个实例退出很简单,只要让其应用程序对InitInstance函数返回FALSE即可。此外,当主窗口退出时 ,应用RemoveProp函数删除我们为其设置的标记。
方法1:
1、在App的InitInstance()中枚举所有窗口,查找本程序实例
HWND oldHWnd = NULL; EnumWindows(EnumWndProc,(LPARAM)&oldHWnd); //枚举所有运行的窗口 if(oldHWnd != NULL) { AfxMessageBox("本程序已经在运行了"); ::ShowWindow(oldHWnd,SW_SHOWNORMAL); //激活找到的前一个程序 ::SetForegroundWindow(oldHWnd); //把它设为前景窗口 return false; //退出本次运行 }
2、添加EnumWndProc窗口过程函数:
//添加的标识只运行一次的属性名 CString g_szPropName = "Your Prop Name"; //自己定义一个属性名 HANDLE g_hValue = (HANDLE)1; //自己定义一个属性值 BOOL CALLBACK EnumWndProc(HWND hwnd,LPARAM lParam) { HANDLE h = GetProp(hwnd,g_szPropName); if( h == g_hValue) { *(HWND*)lParam = hwnd; return false; } return true; }
3、在主窗口的 OnInitDialog()中添加属性
//设置窗口属性
SetProp(m_hWnd,g_szPropName,g_hValue);
方法二:
BOOL CTestApp::InitInstance() { /********************************/ // 用应用程序名创建信号量 HANDLE hSem = CreateSemaphore(NULL, 1, 1, m_pszExeName); // 信号量已存在? // 信号量存在,则程序已有一个实例运行 if (GetLastError() == ERROR_ALREADY_EXISTS) { //关闭信号量句柄 CloseHandle(hSem); //寻找先前实例的主窗口 HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(), GW_CHILD); while (::IsWindow(hWndPrevious)) { // 检查窗口是否有预设的标记? //有,则是我们寻找的主窗 if (::GetProp(hWndPrevious, m_pszExeName)) { // 主窗口已最小化,则恢复其大小 if (::IsIconic(hWndPrevious)) ::ShowWindow(hWndPrevious, SW_RESTORE); // 将主窗激活 ::SetForegroundWindow(hWndPrevious); // 将主窗的对话框激活 ::SetForegroundWindow(::GetLastActivePopup(hWndPrevious)); // 退出本实例 return FALSE; } // 继续寻找下一个窗口 hWndPrevious = ::GetWindow(hWndPrevious, GW_HWNDNEXT); } // 前一实例已存在,但找不到其主窗 // 可能出错了 // 退出本实例 return FALSE; } AfxEnableControlContainer(); //... ... } int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; //设置寻找标记 ljs ::SetProp(m_hWnd, AfxGetApp()->m_pszExeName,(HANDLE)1); m_GameLobbyDlg.Create(this); m_bInit = TRUE; return 0; } CMainFrame::~CMainFrame() { //删除寻找标记 ::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName); }
方法三:
::CreateMutex(NULL,TRUE,m_pszExeName); if(GetLastError()==ERROR_ALREADY_EXISTS) { // 寻找先前实例的主窗口 HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(), GW_CHILD); while(::IsWindow(hWndPrevious)) { if (::GetProp(hWndPrevious,m_pszExeName)) { // 主窗口已最小化,则恢复其大小 if (::IsIconic(hWndPrevious)) ::ShowWindow(hWndPrevious, SW_RESTORE); // 将主窗激活 ::SetForegroundWindow(hWndPrevious); // 将主窗的对话框激活 ::SetForegroundWindow(::GetLastActivePopup(hWndPrevious)); // 退出本实例 return FALSE; } hWndPrevious = ::GetWindow(hWndPrevious, GW_HWNDNEXT); } return FALSE; }
方法四:
另建线程创建一个窗口,记录APP的m_hWnd,直接发线程消息,激活这个窗口.