delphi程序如何防止多实例启动

1,前言

delphi程序运行经常需要采用单实例方式,以免多个实例相互影响。那么,如何防止多实例启动呢?

通常最为常用的是以下两种:

  • 采用mutex互斥量
  • 采用主窗口标题检测

实践发现,无论以上哪种方式,都有坑需要避免。

2,互斥量检测

互斥量检测法,是在delphi程序启动时,创建某个唯一标识的互斥量,如果创建返回的结果是“互斥量已存在”,那么就说明windows系统内存中已经运行了该程序,此时程序退出,就实现了单例运行。

伪代码如下:

program RPMClient;

uses
  Forms,
  dialogs,
  Ufunc in 'Ufunc.pas',
  Umain in 'uMain.pas' {fMain};

{$R *.res}

begin
  Application.Initialize;

  if AppIsRunning then begin
    //showMessage('程序已经启动,请勿重复启动!');
    application.Terminate;
    exit;
  end;

  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.

 

其中的函数AppIsRunning 是关键,实现代码如下:

function AppIsRunning: Boolean;
var
  hmutex:hwnd;
  errno:integer;
begin
  Result := False;

    hmutex:=createmutex(nil,false,pchar('RPMClient'));
    errno:=getlasterror;
    if errno=error_already_exists then
    begin
    Result := True;
    end;
end;

 

显然,以上代码非常常规,没有亮点。但是,如果不注意却可能留下隐患。

注意其中的createmutex函数的第三个参数,采用的是pchar('RPMClient'),一个指向字符串常量的指针。

如果将其改为指向变量的指针,则无法实现单例控制。如下述代码,采用Application.Title代替字符串常量,写法上似乎优雅一点,但是实践发现内存中会出现多实例。

 

hmutex:=createmutex(nil,false,pchar(Application.Title));

 

3,主窗口标题检测

笔者也采用过FindWindow函数执行主窗口标题检测,来避免多实例启动。写法上,类似下面:

function AppIsRunning: Boolean;
var
  hWnd : THandle;
begin

  hWnd:=FindWindow(nil,PChar('RPMClient'));//搜索窗口  API 在windows单元
  Result := hWnd<>0;
end;

 

该方法也很常规,在windows 7下运行正常,但是实践发现,在windows10操作系统中,内存中会出现多个实例。

4,总结

最终,笔者采用了createmutex函数执行单例控制,注意第三个参数采用字符串常量即可。在windows7和10下,均正常。

为什么FindWindow在win10下不能正常检测呢?笔者不明白,猜测有可能是win10有时会把应用变为后台进程的缘故。

至于createmutex为什么第三个参数不能是变量指针,笔者也不明白,猜测有可能是变量指针比常量指针更耗时,导致启动时来不及控制?

 

总之,单例控制方法多且简单,但是如何使用还是有不少具体细节的,稍不注意就出现bug。笔者总结以上现象,以供同行借鉴。

posted @ 2021-12-08 09:31  jack0424  阅读(183)  评论(0编辑  收藏  举报