当我们在做一些管理平台类的程序(比如Windows的任务管理器)时,往往需要限制程序只能打开一个实例。解决这个问题的大致思路很简单,无非是在程序打开的时候判断一下是否有与自己相同的进程开着,如果有,则关闭自身,否则正常运行。
  但是,问题就出在如何判别是否有一个与自己相同的进程开着上面。我在网上搜索了一下相关的文章,发现对于这个问题的解决不外乎以下几种方式:
  1、在进程初始化时使用::CreateMutex创建一个互斥对象,通过检测互斥对象是否已存在来确定该程序是否已经运行。
  该方式的确可以很容易的实现判别程序实例是否已存在,只需要在InitInstance方法开头添加以下语句:
m_hUnique = ::CreateMutex(NULL, FALSE, UNIQUE_ID);
if (GetLastError() == ERROR_ALREADY_EXISTS) return FALSE;

  UNIQUE_ID为具有唯一性的字符串,一般可以用VC++为主程序头文件自动生成的包含标识宏(就是.h文件顶上的那一长串宏定义),当然,也可以用工具自己手动生成,随君所好了^^。要注意的是别忘了在ExitInstance方法中用 CloseHandle(m_hUnique) 将该互斥对象关闭。但这种方式存在一个很大的问题,就是很难获取已打开程序实例的主窗口句柄。而我们绝大多数时候,都需要将那个程序实例的主窗口激活。为了获取主窗口句柄,就需要再用到后面提到的其他方法。
  2、遍历所有已经打开的进程主窗口,比较窗口标题,如果找到满足条件的标题,则表示程序已经运行,并激活该窗口。
  这种方式虽然可以找到程序的主窗口,但问题明显:A.如果窗口标题经常变化怎么办(比如标题中会带有打开文档的文件名)?B.如果其他程序的主窗口标题恰好与该程序的相同怎么办?
  第一个问题可以通过写注册表或者写INI文件的方式来解决。即当主窗口标题改变时,将新标题写入注册表或者INI文件。不过这种解决方式也忒麻烦了吧-_-||   第二个问题就麻烦了,至少我还没有找到好的解决方案。如果非要说一个,那我提议你“想尽办法”“不择手段”的将窗口标题设的和别的程序绝对不同。不过估计搞定了这步,你半条命也快没了。
  3、用::SetProp给主窗口添加一个具有唯一性的属性值,以便在进程初始化的时候可以通过遍历所有窗口的该属性来判断。
  添加属性值的代码一般可以放在InitInstance方法的最后,如下:
::SetProp(m_pMainWnd->m_hWnd, "UNIQUE_ID", (HANDLE)UNIQUE_ID);

  UNIQUE_ID是一个具有唯一性的整数值(为什么不能用字符串?因为字符串的比较需要将字符串读取出来,而这儿只能记录字符串地址,在别的程序里这个地址无意义,所以无法读出这个字符串)。这种方式仅有的问题就出在如何确定该整数值是具有唯一性的。我们后面提出的解决方法,就是在这种方法的基础上发展出来的。
  在总结了上述几种方式的利弊之后,我发现,只需要为程序建立一个具有唯一性的整数值,一方面可以通过这个值是否存在来判断程序是否已经运行(::CreateMutex其实也是类似的概念),另一方面可以通过将这个值赋给主窗口,以便能够找到已打开的程序实例的主窗口句柄。于是,ATOM量便派上用场了(ATOM变量类型等同于WORD,因而是一个整数值)。
  ATOM量本质上就是散列表的键标识符,其对应键值为一个字符串。每个程序都有自己的ATOM量表,同时Windows也有一个全局的ATOM表。我们要用的方法就是,为程序创建一个全局的ATOM量,通过这个量是否存在来判断程序是否已经运行,并通过将这个量作为属性值添加到主窗口来标识这个主窗口。具体过程如下:
  1、给主程序App类添加一个ATOM类型的成员变量:m_aAppId,作为程序ID。
  2、在InitInstance方法开头添加以下代码(UNIQUE_ID是具有唯一性的字符串宏):
m_aAppId = ::GlobalFindAtom(UNIQUE_ID); //查找程序ID是否存在
if (m_aAppId) //程序ID存在,激活已打开的程序实例的主窗口
{
HWND hWnd = ::GetWindow(::GetForegroundWindow(), GW_HWNDFIRST);
for (; hWnd; hWnd = ::GetWindow(hWnd, GW_HWNDNEXT))
{
if ((ATOM)::GetProp(hWnd, "APP_ID") == m_aAppId)
{
if (::IsIconic(hWnd)) ::ShowWindow(hWnd, SW_RESTORE); //还原最小化的窗口
::SetForegroundWindow(hWnd); //激活窗口
m_aAppId = 0; //赋值0是为了防止ExitInstance中将找到的ATOM量删除
break;
}
}
return FALSE;
}
else //程序ID不存在,创建程序ID
{
m_aAppId = ::GlobalAddAtom(APP_ID);
}

  3、在InitInstance方法最后为主窗口添加标识属性:
::SetProp(m_pMainWnd->m_hWnd, "APP_ID", (HANDLE)m_aAppId);

  4、在ExitInstance方法中添加下面代码以删除程序ID:
if (m_aAppId) ::GlobalDeleteAtom(m_aAppId);

心得:该方法所用到的ATOM量是一个应用广泛的技术,如::CreateMutex、::SetProp等API函数都间接用到了ATOM量。利用它,我们可以做很多需要用到唯一性验证的事情。

转载于:https://www.cnblogs.com/flying_bat/archive/2007/07/14/818052.html

限制程序只打开一个实例(转载)相关推荐

  1. 转:让程序只运行一个实例的四种方法

    先留着以后有用. 让程序只运行一个实例的四种方法 综述:让一个程序只运行一个实例的方法有多种,但是原理都类似,也就是在程序创建前,有窗口的程序在窗口创建前,检查系统中是否已经设置了某些特定标志了,如果 ...

  2. 让Java程序只运行一个实例

    一个程序可以在内存里面存在多个运行实例,比如,你可以打开多个微软的Word程序.但是,有些时候我们需要控制程序运行的实例只有一个,也就是说,该程序同一时刻在内存里面运行的只有一个实例.这样当这个程序在 ...

  3. Qt应用程序只运行一个实例

    在实际应用中,我们经常需要让应用程序只有一个实例,再打开新的文档或者页面时,只是替换现在的窗口或者新打开一个标签,而不是重新启动一次应用程序.Qt中是否可以做到这样呢,答案是肯定的,因为Qt本身可以直 ...

  4. C#实现让程序只能打开一个实例(总结3方法)

    代码:                         //=====创建互斥体法:=====             //bool blnIsRunning;             //Mutex ...

  5. 只运行一个实例的方法

    在VC++ 中编程中,只运行一个实例的方法主要有两类: 1 遍历当前的所有窗口,查找相同的实例.为了便于查找,一般要事先设一个查找标志. 2 利用系统提供的互斥对象或信标,直接让系统抑制重复的实例. ...

  6. 只运行一个实例的写法

    有时我们需要只允许运行应用程序的一个实例,当进程启动时,如果发现应用程序的一个实例在运行,就自动停止运行.我们通常通过Mutex互斥体在Main函数中实现,通常的写法是: [STAThread] st ...

  7. 在同一个浏览器上打开同一个网址只打开一个窗口的方法

    具体问题看图吧,我自己也说不清楚 具体操作如下: target属性的功能之一是可以在同一个浏览器中只打开被标记相同的网页窗口 利用这一功能可以实现以上问题. target属性链接地址:http://w ...

  8. 限制_blank属性只打开一个新页签

    我们在开发过程中点击a标签或按钮跳转新页签时,如果再次点击该链接或按钮,还会重新打开一个页面.这样的交互效果不是很友好. 使用如下方式可以限制打开新的页签,只保留一个新页签. 1.使用target = ...

  9. python怎么打开一个窗口_python – 使按钮一次只打开一个窗口(通过关闭Toplevel窗口启用按钮)...

    我希望NewWinButton一次只创建一个新窗口,这意味着如果 if NewWin.winfo_exists() == 1: NewWinButton.config(state='disabled' ...

最新文章

  1. ggcor包的安装与绘图示例
  2. Vue — 第六天(vue-cli-介绍)
  3. c语言 抽奖算法,腾讯2018校招笔试!抽奖算法思路c++实现!进入鹅厂真的简单!...
  4. 使用FileZilla搭建简单的FTP
  5. 统计学附录,F分布和t分布表
  6. 1.分布式服务架构:原理、设计与实战 --- 分布式微服务架构设计原理
  7. 手把手教你sql触发器的使用
  8. 写在博士旅程之前|博士第一年|博士第三年|博士第四年
  9. 分享一个去水印接口,完全免费,早点下手啊
  10. PLC可编程控制器概述
  11. 防火墙的策略路由PBR
  12. Excel技巧 - Date函数日期转换
  13. flutter 单线程异步 及 isolate 使用过程遇到的问题
  14. 中国第一大微商TST涉嫌传销案听证会结束
  15. 联想电脑G40无法使用 非要睡眠后才能启用wifi
  16. Android开发详解之App升级程序一点通
  17. HTML+JS+websocket 实现联机“游戏王”对战(一)
  18. 博世传感器调试笔记(一)----加速度传感器BMA253
  19. arduino和stm32哪个更好学?
  20. WebRTC学习笔记七 pion/webrtc

热门文章

  1. 悉尼大学计算机研究生学制,悉尼大学研究生学制
  2. 控制行输入以下两句命令16倍速播放青年大学习
  3. 1115 Counting Nodes in a BST
  4. (C++) A+B 输入输出练习IV 每行的第一个数N,表示本行后面有N个数。 如果N=0时,表示输入结束,且这一行不要计算。
  5. 物联网技术与应用(第1-2课时)(cont.)
  6. centos 安装 NTFS支持
  7. 前后端分离的思考与实践(三)
  8. ssh-keygen
  9. DX11 preprocessor Dynamic shader linkage
  10. com.mysql.jdbc.PacketTooBigException: Packet for query is too large (1169 1024)