作者:mmmmmmmmmm0000 | 来源:互联网 | 2023-09-05 19:17
1,前言
delphi程序运行经常需要采用单实例方式,以免多个实例相互影响。那么,如何防止多实例启动呢?
通常最为常用的是以下两种:
实践发现,无论以上哪种方式,都有坑需要避免。
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。笔者总结以上现象,以供同行借鉴。