一次运行一个实例

《金山词霸》已经运行了的情况下,再次点击《金山词霸》的图标,那么它不会再运行另外一个《金山词霸》,而是将已有的《金山词霸》给激活,始终只能运行一个《金山词霸》的实例。
在我们的程序当中如果要实现类似《金山词霸》的功能,就要解决两个问题,首先是要判断该程序已有一个实例在运行,其次是要将已运行的应用程序实例激活,同时退出第二个应用程序实例。
   对于第一个问题,我们可以通过设置命名互斥对象或命名信标对象,在程序启动的时候检测互斥对象或信标对象,如互斥对象或信标对象已存在,则可以判断此程序已有一个实例正在运行。
   第二个问题是如何找到已经运行的应用程序实例,如果我们能够找到已运行实例主窗口的指针,即可调用SetForegroundWindow来激活该实例。我们可以通过两种形式找到已运行实例的主窗口,一种形式是通过调用FindWindowEx去查找正在运行的窗口的句柄,这种方式用得比较多一些,而本文通过另一种形式去查找正在运行的窗口的句柄。通过调用SetProp给应用程序主窗口设置一个标记,用GetDesktopWindow 可以获取Windows环境下的桌面窗口的句柄,所有应用程序的主窗口都可以看成该窗口的子窗口,接着我们就可以用GetWindow函数来获得这些窗口的句柄。然后再用Win32 SDK函数GetProp查找每一个应用程序的主窗口是否包含有我们设置的标记,这样就可以找到我们要找的第一个实例主窗口。
1.在应用程序类InitInstance()函数中判断是否已有一个应用程序实例正在运行。
BOOL CServer2App::InitInstance()
{
 //创建命名信标对象(信号量)
 HANDLE hSem=CreateSemaphore(NULL,1,1,_T("jzny农机调度Server2"));
 //信标对象创建成功
 if(hSem)
 {
  //信标对象已经存在,则程序已有一个实例在运行
  if(ERROR_ALREADY_EXISTS==GetLastError())
  {
   //关闭信号量句柄。
   CloseHandle(hSem);
   //获取桌面窗口的一个子窗口
   //HWND GetDesktopWindow(VOID)函数功能:该函数返回桌面窗口的句柄。桌面窗口覆盖整个屏幕。桌面窗口是一个要在其上绘制所有的图标和其他窗口的区域。
   HWND hWndPrev=::GetWindow(::GetDesktopWindow(),GW_CHILD); 
   //判断窗口是否有我们预先设置的标记,如有则是我们寻找的窗口并将它激活
   //BOOL isWindow(HWND hWnd);返回值:如果窗口句柄标识了一个已存在的窗口,返回值为非零;如果窗口句柄未标识一个已存在窗口,返回值为零
   while(::IsWindow(hWndPrev))
   {
    if(::GetProp(hWndPrev,_T("jzny农机调度")))
    {
     //如果主窗口已最小化,则恢复其大小。
     //if (::IsIconic(hWndPrev))
     //{
      ::ShowWindow(hWndPrev,SW_RESTORE);
     //}
     //将应用程序的主窗口激活。
     ::SetForegroundWindow(hWndPrev);
     return FALSE;
     //退出实例。
    }
    //继续寻找下一个窗口。
    hWndPrev = ::GetWindow(hWndPrev,GW_HWNDNEXT);
   }
   AfxMessageBox(_T("已有一个实例在运行,但找不到它的主窗口!"));
  }
 }
 else
 {
  AfxMessageBox(_T("创建信标对象失败,程序退出!"));
  return FALSE;
 }

 CWinApp::InitInstance();

 // 标准初始化
 // 如果未使用这些功能并希望减小
 // 最终可执行文件的大小,则应移除下列
 // 不需要的特定初始化例程
 // 更改用于存储设置的注册表项
 // TODO: 应适当修改该字符串,
 // 例如修改为公司或组织名
 SetRegistryKey(_T("应用程序向导生成的本地应用程序"));


 WSADATA wsa;
 //加载winsock动态链接库
 int ret = WSAStartup(MAKEWORD(2,0),&wsa);//成功加载返回值应为0

 VERIFY(ret == 0);


 CServer2Dlg dlg;
 m_pMainWnd = &dlg;
 INT_PTR nResponse = dlg.DoModal();
 if (nResponse == IDOK)
 {
  // TODO: 在此处放置处理何时用“确定”来关闭
  //  对话框的代码
 }
 else if (nResponse == IDCANCEL)
 {
  // TODO: 在此放置处理何时用“取消”来关闭
  //  对话框的代码
 }


 VERIFY(WSACleanup() != SOCKET_ERROR);

 // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
 //  而不是启动应用程序的消息泵。
 return FALSE;/**/
}

2.在框架类的OnInitDialog()函数中设置查找标记。

BOOL CServer2Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();

.........

 //设置查找标记。
 ::SetProp(m_hWnd,_T("jzny农机调度"),(HANDLE)1);

.........

}

 

 

3.在程序退出是删除设置的标记,在框架类中响应WM_DESTROY消息,进行处理。

void CServer2Dlg::OnDestroy()
{
 CDialog::OnDestroy();
 //删除所设置的标记。
 ::RemoveProp(m_hWnd,_T("jzny农机调度"));
    //PostMessage(WM_QUIT);
 //TODO: 在此处添加消息处理程序代码
}


4、信标对象
  信标对象,也叫信号灯,用于限制资源访问数量,他包含一个引用计数,一个当前可用资源数,一个最大可用资源数。如果当前可用资源数大于0,信标对象处于有信号状态。当可用资源数等于0,信标对象处于无信号状态。

和信标对象相关的函数:

HANDLE  CreateSemaphore(LPSECURITY_ATTRIBUTES  lpSemaphoreAttributes, 
		LONG  lInitialCount, 
		LONG  lMaximumCount, 
		LPCTSTR  lpName);

BOOL  ReleaseSemaphore(HANDLE  hSemaphore, 
		LONG  lReleaseCount, 
		LPLONG  lpPreviousCount);

  函数CreateSemaphore的参数1为NULL,参数2为当前可用资源初始值,参数3为最大可用资源数,参数4为名字。当参数2的值等于0时,信标对象处于无信号状态,这时内核将调用等待函数的线程置于睡眠状态,如果参数2的值大于0,信标对象处于有信号状态,这时内核将调用等待函数的线程置于运行状态,并将信标对象的当前可用资源数减1。函数ReleaseSemaphore的参数1为信标对象的句柄,参数2为要释放的资源数,参数3返回原来可用资源数,调用此函数将当前可用资源数加上参数2的值。当一个线程访问完可用资源后,应该调用ReleaseSemaphore函数使当前可用资源数递增。要在不同进程中访问同一信标对象,调用CreateSemaphore函数并传递信标对象的名称,得到已经在其它进程创建的信标对象的句柄。CE下没有OpenSemaphore函数。另外我还要说明一点,等待函数默认将信标对象的当前可用资源数减1,但线程可能一次使用多个资源,这就可能出现问题了。为避免问题出现,应该遵守一个线程只使用一个资源的原则。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值