文档章节

限制程序只打开一个实例(转载)

rise-worlds
 rise-worlds
发布于 2016/06/20 13:37
字数 1428
阅读 3
收藏 0

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

当我们在做一些管理平台类的程序(比如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量。利用它,我们可以做很多需要用到唯一性验证的事情。

本文转载自:http://www.cnblogs.com/flying_bat/archive/2007/07/14/818052.html

rise-worlds

rise-worlds

粉丝 3
博文 1760
码字总数 1380
作品 0
深圳
程序员
私信 提问
python 面向对象高级编程之使用__slots__

python学习笔记,特做记录,分享给大家,希望对大家有所帮助。 使用slots 正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语...

展菲
07/08
0
0
绕过SQL Server的登录触发器限制

  在做渗透测试任务时,我们常常会碰到一些直连SQL Server数据库的桌面应用。但偶尔也会碰到一些后端为SQL Server的应用,并且其只允许来自预定义的主机名或应用程序列表的连接。这些类型的...

FreeBuf
2018/07/15
0
0
小文——在C# WinForm中如何使当前应用程序只允许启动一个实例

我们在创建C# WinForm应用程序时,有时需要限制用户在同一时间只允许启动一个应用程序实例,例如安装程序包、系统扫描工具等等。当应用程序的一个实例已经处于运行状态时,如果用户企图再启动...

浣熊干面包
2017/11/18
0
0
使用VisualVM查看Java Heap Dump

浏览Heap Dump 可以使用VisualVM浏览heap dump文件的内容,从而快速查看在堆中分配的对象。Heap dumps在主窗口的heapdump子标签页中显示。你可以打开保存在本地的heap dump文件(.hprof)或者...

银月光海
2016/03/31
113
0
组策略应用之二——限制客户端软件安装及使用

我们企业网络中,经常会出现有用户使用未授权软件的情况。比如,有些可以上网的用户使用QQ等聊天工具;而这往往是BOSS们所不想看到的东西。所以限制用户使用非授权软件的重任就落到我们IT部头...

技术小胖子
2017/11/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

为什么面试必问线程状态?你的回答满分了吗

看很多同学的面经、网上的面试资料,都不约而同的提到了一个基础问题:“你知道线程有几种状态吗?状态之间的扭转是怎样的?”,有准备的同学都知道有五种:New(新建)、Runnable(可运行)...

Z_J_H
28分钟前
4
0
如何保障云上数据安全?一文详解云原生全链路加密

点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》 本文节选自《不一样的 双11 技术:阿里巴巴经济体云原生实践》一书,点击上方图片即可下载! 作者 李鹏(壮怀)阿里云容器服务高...

阿里巴巴云原生
28分钟前
3
0
获取数组的第一个元素

我有一个数组: array( 4 => 'apple', 7 => 'orange', 13 => 'plum' ) 我想获得此数组的第一个元素。 apple 预期结果: apple 一个要求: 它不能通过引用传递来完成 ,所以array_shift不是一......

javail
30分钟前
4
0
哈希情史知多少

<p align="right">——日拱一卒,不期而至!</p> 简介 hash是我们工作中经常听到的词,比如哈希表、哈希函数、hashCode、HashTable、HashMap等等,那么它们之间到底有怎样的爱恨情仇呢?来一...

彤哥读源码
36分钟前
4
0
SpringCloud 学习(5) --- Zuul(一)基本概念、配置

[TOC] Spring Cloud eureka:注册中心 服务端:提供注册 客户端:进行注册 ribbon:负载均衡(集群) Hystrix:熔断器,执行备选方案 Feign:远程调用 Zuul:网关,统一入口。 1.1、一夫当关,...

庭前云落
39分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部