28.VC(custom)-控制一个程序只能有一个进程(单例随手代码)

来源:互联网 发布:java基础教程 csdn 编辑:程序博客网 时间:2024/05/11 00:58

EnumWindows的使用

BOOL EnumWindows( WNDENUMPROClpEnumFunc, LPARAMlParam);该函数枚举所有屏幕上的顶层窗口,并将窗口句柄传送给应用程序定义的回调函数,其中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()中添加属性

<span style="color:#000000;">//设置窗口属性SetProp(m_hWnd,g_szPropName,g_hValue);</span>

 

方法二:

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,直接发线程消息,激活这个窗口.