你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 编程语言
防止程序开启多个实例的终结篇
 

 要:本文主要对多种防止程序开启多个实例的方案进行总结,得出三种行之有效的方案,并对之进行比较分析。还示范如何使用Callback函数枚举窗口。

关键字:实例,Mutex,枚举,Callback

 

在开发的软件的过程中,我们通常会给程序加上防止程序启动一个以上实例的功能,以免用户打开程序的多个实例。要达到这样的目的,方法是很多,它们的针对性也略有不同。经过笔者在实际开发中的摸索,总结出以下三种方便实用方案(开发环境为Borland Delphi):

一、CreateMutex函数

CreateMutex函数可为程序建立以某一字符串命名的Mutex(互斥)对象,这样我们就可以在程序启动的时候,以该Mutex对象是否存在来判断程序是否运行。该函数的用法很简单,共有三个参数:lpMutexAttributesbInitialOwnerlpName 。第一个参数在Win95环境下可忽略,即传入nil值;第二个参数是布尔型参数,其取值确定所建立Mutex对象是否指定所有者(ownership),所以要用True;第三个参数就是所建立Mutex对象的名字,必须指定一字符串。

具体实现方法:在程序启动时调用函数CreateMutex 建立以某一字符串命名的Mutex对象,如果在CreateMutex调用前,以该字符串命名的Mutex已存在,则调用CreateMutex函数后系统会返回ERROR_ALREADY_EXISTS错误消息,我们就是以此错误信息为依据判断程序是否已运行的。详细程序如下:

procedure Running_CreateMutex;

var MutexHandle: THandle;

begin

     //建立一个用'MyTestApp'来命名的唯一标记

     MutexHandle := CreateMutex(nil, TRUE, 'MyTestApp');

     if MutexHandle <> 0 then//如果建立失败,

     begin  //错误信息是ERROR_ALREADY_EXISTS,则表明程序已运行;

        if GetLastError = ERROR_ALREADY_EXISTS then

        begin

           MessageBox(0,'Application is running!','Test', mb_IconHand);

           CloseHandle(MutexHandle);

           Halt;//结束本程序

        end

     end;

end;

以上方法的缺点是很明显的,就是在发现程序已运行时,只能简单的提示一下,令用户感到莫名其妙。所以,在很多时候我们需要的是,在发现程序有一个实例已运行时,就将该已运行的实例调出前台,显示在用户面前,这样更加贴近人的逻辑思维、更体贴用户。这样,以上方法就显得力不从心了,必须用以下方法:

二、用FindWindow函数

FindWindow函数可对窗口的类名或标题进行查找,如果成功找到窗口,则返回该窗口的句柄(Handle)。得到窗口的句柄后,我们就可用ShowWindow函数将窗口调出前台。该函数有两个参数:lpClassNamelpWindowName ,它们分别是要查找窗口的类名和标题。

 

procedure Running_FindWindows;

var hWnd:THandle;

begin

hWnd:=FindWindow(nil,'MyTestApp');//搜索窗口

if hWnd<>0 then                   //如果找到指定的窗口

begin

   if IsIconic(hWnd) then          //如果已被最小化

      ShowWindow(hWnd,SW_RESTORE)   //则把它恢复

   Else               //如果窗口被其他窗口遮住,则将它提到前景来

      SetForegroundWindow(hWnd);

   Halt;        //结束本程序

 end;

end;

FindWindow函数可通过指定一个窗口的类名进行查找,也可指定一个窗口的标题文字(字符串)进行查找,由或者两者相结合查找,但必须指出,这里的窗口必须是应用程序的主窗口。

但是,有些时侯我们是不能完全确定类名或窗口标题的。例如我们常用的Microsoft Word,它的主窗口标题就是随着您打开不同的文档而变化的,Photoshop等软件也是这样的。那么上面的方法也达不到我们的目的,看看以下方法。

三、用EnumWindows函数

    EnumWindows函数可枚举系统屏幕中的所有窗口,我们可逐一提取其标题字符串,然后检查该字符串是否含有某一子字符串,如Microsoft Word的主窗口标题是以字符串Microsoft Word -加打开文档的文件名而定的,也就是说,无论你打开什么文档,Word主窗口的标题必定含有字符串Microsoft Word -。所以我们可以此来判断指定的窗口是否存在,进而判断程序是否运行。要用EnumWindows函数,我们必须用stdcall声明一个Callback 函数,其固定的入口参数是:(Wnd: HWnd;LPARAM: lParam),以此Callback 函数的指针作为EnumWindows函数的第一参数,而第二参数在这里不用理会。以下是一Callback 函数的例子,其中主窗口的标题是MyTestApp

function EnumApps(Wnd: HWnd;LPARAM: lParam): boolean; stdcall;

var WndCaption : array[0..254] of char;

begin

    Result := True;

    GetWindowText(Wnd,@WndCaption, 254);//获取窗口的Caption

   if(Pos('MyTest',WndCaption)>=1)then  //如果窗口的Caption含某子字符串

    begin

       if IsIconic(Wnd) then        //如果已被最小化

          ShowWindow(Wnd,SW_RESTORE)//则把它恢复

       else    //如果窗口被其他窗口遮住,则将它提到前景来

          SetForegroundWindow(Wnd);

       Result:=False;

       Halt; //结束本程序

    end;

end;

    完成了上面的函数后,再用函数EnumWindows(@EnumApps,0)调用它就大功告成了。

 

procedure Running_EnumWindows;

begin

     EnumWindows(@EnumApps,0);    //枚举所有窗口

end;

    有一点是要注意的,在调试第二和第三种方案时,必须先把Delphi关闭再运行本程序,因为Delphi在设计阶段的也存在同样标题的窗口(设计窗口)。

    以上程序在Delphi 4.0 、简体中文Windows 98编译通过。另外,本文所阐述的三种方案用到的是纯Windows API函数,所以不单适用于Delphi,同样可用于VBVC++Borland C++ Builder等编程环境。

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089