限制程序只打开一个实例

转载 2006年06月19日 17:46:00
【作者:Fictiony (fictiony@china.com)】
【转载请注明出处】 
  当我们在做一些管理平台类的程序(比如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量。利用它,我们可以做很多需要用到唯一性验证的事情。 

相关文章推荐

Flex 如何限制客户端只运行一个application实例

用flex做web应用,就必须处理web应用的一个常见问题,即在同一台电脑上用不同账号登陆web应用,由于火狐了、ie7以上版本等浏览器sessionid是同一个,因此会发生a、b用户共享同一sess...

C#限制程序只运行一个

Java Swing 每次打开只运行一个实例,并激活任务栏里的程序

import java.awt.AWTException; import java.awt.Dimension; import java.awt.Image; import java.awt.Menu...
  • lovoo
  • lovoo
  • 2016-09-14 21:40
  • 1843

一个无聊的自动提款机程序---改进版——限制用户,超时特征

/* * PLAY_AGAIN.C * 功能:实现组件2 * * 对用户显示提示问题 * 接受输入 * 如果是y,返回0 * 如果是n,返回1 * */ #...

Winform and WPF 第二遍双击快捷方式或应用程序打开原来的应用程序而不新建一个实例[进程通信 1]

今天工作忙完之余,总结一些Winform WPF应用程序,第二遍双击快捷方式或应用程序打开原来的应用程序而不新建一个实例。 下面两个例子分别用了两种不同的方式实现了进程间的通信。   ------...
  • wzhiu
  • wzhiu
  • 2012-06-19 15:16
  • 3020

限制程序打开个数

内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)