Delphi中如何防止运行一个应用程序的多个实例

转载 2007年09月11日 21:57:00

Delphi中如何防止运行一个应用程序的多个实例


吴淑华
01-5-31 下午 01:56:54


实际应用中,程序设计人员有时希望某一时刻只运行应用程序的单个实例。或许是因为应用程序需要访问专用的特殊资源,如访问调制解调器或是CD-ROM驱动器,或许是因为应用程序需占用大量的系统资源,为保证其工作正常只能运行单一实例。
无论何种原因,如果在运行第二个实例时,简单地终止先前的实例,那么程序就会显得十分粗糙。为避免这个问题,一般要求在第二个实例终止前,把第一个实例的窗口送到栈顶。一种Win16的方法是简单地检测应用程序先前实例的句柄,如果其值不为NULL,就可以标识其他的实例。然而,在Win32中,这个值总为NULL。因此,只能选用其他方法。
这里提供的方法不依赖于应用程序具体的窗口标题也不依赖于登录窗口类。不但可以终止第二个实例,而且可以把第一个实例带到前台。下面本文结合具体实例,详细介绍一下实现步骤,并总结实现这些步骤的技术要点。
一、实现步骤
1.程序功能描述
该程序除主窗口MainForm外,还包括用户登录子窗口(密码检查窗口)LoginForm。程序启动后,首先需通过用户登录后才能进入应用程序主窗口。如果程序不包括用户登录子窗口,即程序启动后直接进入主窗口,实现步骤中需去掉步骤3和步骤5,会更简单一些。本实例之所以包括登录窗口,是考虑到实际应用中有时会遇到类似情况,如果了解了后者如何实现,当然也就掌握了前者的实现方法。
2.编辑TMainForm.FormCreate(Sender: TObject)
首先检查应用程序其他的实例是否启动。方法是创建一个mutex(Mutex很象临界区,除了在访问多进程时能同步数据外。)如果所命名的mutex已经存在,就说明应用程序的另一个实例在运行。通过将唯一的一个名字传送给CreateMutex可以命名mutex。在此以应用程序名作为第三个参数。代码如下:
procedure TMainForm.FormCreate(Sender: TObject);
var
mutexName: String;//互斥元名
hPrevWnd, hData: HWND; //hPrevWnd---窗口句柄, hData---GetProp返回的属性值
result: TModalResult;
LoginDlg: TLoginForm;
begin
mutexName := Application.ExeName; //以应用程序名作为互斥名
 
//创建互斥元.如果互斥元已经存在,这就是应用程序的第二个事例.
//注意:当应用程序结束时,互斥元自动关闭.
CreateMutex(Nil, TRUE, PChar(mutexName));
if (GetLastError() = ERROR_ALREADY_EXISTS) then
begin
...
end;
...
end;
3.编辑TLoginForm.FormCreate
由于该实例刚运行时要先调用登录子窗体LoginForm,只有通过了密码检查后才能启动应用程序主窗体MainForm。为了避免该程序的前一个实例刚运行到登录窗口时,就运行程序的第二个实例,采用标记登录窗口的方法,并在步骤4中对该标记进行判断。代码如下:
procedure TLoginForm.FormCreate(Sender: TObject);
begin
//进行窗口初始化
......
 
//设置窗口属性,以便在该应用程序系统启动时进行判断
SetProp(Handle, PChar(Application.ExeName), 2);
end;
4.寻找先前实例
当断定应用程序的另一个实例正在运行后,首先把先前的实例调到前台并给予焦点,并最大化显示该窗口。
在此,通过调用SDK的函数SetProp添加一个和窗口句柄组合成对的字符串/数据句柄来标记一个窗口。当检查出其他实例正在运行时,可通过搜索所有顶层窗口的标记,找到先前应用程序的主窗口。对每一个窗口来说,通过调用SDK的GetProp函数,可以找到先前实例的标记。如果窗口包含该标记,则找到了主窗口。窗口找到后,最大化并送到前台。具体代码如下:
procedure TMainForm.FormCreate(Sender: TObject);
var
mutexName: String;//互斥元名
hPrevWnd, hData: HWND; //hPrevWnd---窗口句柄, hData---GetProp返回的属性值
result :TModalResult;
LoginDlg :TLoginForm;
begin
mutexName := Application.ExeName; //以应用程序名作为互斥名
 
//创建互斥元.如果互斥元已经存在,这就是应用程序的第二个事例.
//注意:当应用程序结束时,互斥元自动关闭.
CreateMutex(Nil, TRUE, PChar(mutexName));
if (GetLastError() = ERROR_ALREADY_EXISTS) then
begin
//查找该应用程序的前一个主窗口句柄.
hPrevWnd := GetDesktopWindow();
hPrevWnd := GetWindow(hPrevWnd, GW_CHILD);
while (IsWindow(hPrevWnd)) do
begin
//判断此窗口属性标志是否与设置的主窗口或登录窗口标志相符.
hData := GetProp(hPrevWnd, PChar(mutexName));
if (hData = 1) or (hData = 2) then
begin
//判断是主窗口还是登录窗口,并将窗口获得焦点.
if hData = 1 then //主窗口
ShowWindow(hPrevWnd, SW_MAXIMIZE)
else //登录窗口
ShowWindow(hPrevWnd,SW_RESTORE);
SetForegroundWindow(hPrevWnd);
 
//如果此窗口有弹出窗口,设置焦点到弹出窗口.
SetForegroundWindow(GetLastActivePopup(hPrevWnd));
break;
end else
//没有找到窗口,转到窗口列表中下一个窗口.
hPrevWnd := GetWindow(hPrevWnd, GW_HWNDNEXT);
end;
Application.Terminate;
exit;
end;
5.删除登录窗口先前实例标识符
删除步骤3中创建的窗口标记。
procedure TLoginForm.FormDestroy(Sender: TObject);
begin
RemoveProp(Handle, PChar(Application.ExeName));
end;
6.删除主窗体先前实例表示符
删除步骤4中创建的窗口标记。
procedure TMainForm.FormDestroy(Sender: TObject);
begin
RemoveProp(Handle, PChar(Application.ExeName));
end;
 
二、技术要点
1.定位先前窗口,使用mutex比用FindWindow更安全,因为在实例完成创建主窗口之前,应用程序的第二个实例有可能启动。使用mutex可以防止此类情况发生。
2.要寻找应用程序先前实例主窗口,可用FindWindow寻找有标题的窗口。该方法需要知道主程序窗口的标题,但如果应用程序动态更新标题,则该方法不适用。也可用FindWindow来寻找具有具体注册窗口类的窗口。但该方法需要注册用户自己的窗口类,而且以后版本升级时,可能需要修改代码。而采用SDK的SetProp函数来“标记”窗口可以避免上述问题。
3.通过调用API函数GetDesktopWindow和GetWindow可以搜索所有的顶层窗口。再通过判断窗口标记找到先前实例窗口。
 

Delphi防止同时出现多个应用程序实例

多实例指同时有同一个应用程序的多个副本在运行。同一个应用程序的多个副本可以相互独立地同时运行,是Win32操作系统提供的一个功能。但有时,我们可能希望用户启动应用程序后就不再启动它的别的副本。比如某种...
  • Tercel99
  • Tercel99
  • 2008年02月13日 09:24
  • 1524

利用命名对象来防止运行一个应用程序的多个实例

原始方法:   在应用程序入口(_tmain或_tWinMain)函数中创建(Create*)一个命名对象(具体创建什么类型无关紧要),Create*返回后,再调用一下GetLassError。如果返...
  • zhaogang1993
  • zhaogang1993
  • 2016年08月21日 20:32
  • 384

【转】delphi程序只允许运行一个实例的三种方法:

一、        创建互斥对象    在工程project1.dpr中创建互斥对象 Program project1 Uses Windows,Form, FrmMain in 'Frm...
  • beyondqd
  • beyondqd
  • 2012年12月05日 11:15
  • 6281

保证应用程序只有一个实例在运行——VS2008

让一个程序只运行一个实例的方法有多种,但是原理都类似,也就是在程序创建前,有窗口的程序在窗口创建前,检查系统中是否已经设置了某些特定标志了,如果有说明已经有一个实例在运行了,则当前程序通知用户怎样怎样...
  • u011981018
  • u011981018
  • 2015年01月29日 16:43
  • 600

Qt单实例程序(防止程序多开)

使用QLocalServer,QLocalSocket实现单实例进程,当已经存在相同进程时且窗口未激活(遮挡,最小化,托盘)时,激活进程主窗口。 参考文章:http://blog.csdn.net/p...
  • sunflover454
  • sunflover454
  • 2015年12月29日 14:47
  • 1344

程序只启动一个实例的几种方法

    有些时候,我们要求一个程序在系统中只能启动一个实例。比如,Windows自带的播放软件Windows Medea Player在Windows里就只能启动一个实例。原因很简单,如果同时启动几个...
  • starlee
  • starlee
  • 2007年03月20日 09:26
  • 11706

C#建立了一个windows程序,如何防止多个程序实例运行?

1.可以在Main函数中如下操作:[STAThread]static void Main(){System.Diagnostics.Process[] ps = System.Diagnostics....
  • songkexin
  • songkexin
  • 2007年01月21日 05:50
  • 948

WPF 防止程序多次打开运行

/// /// App.xaml 的交互逻辑 /// public partial class App : Application { privat...
  • lwwl12
  • lwwl12
  • 2018年01月06日 10:12
  • 192

Java 只运行一个实例

查了一下资料,Java 只运行一个实例的方法有下面三种: 1、占用端口(有效,但是有端口被挪用的缺陷) 2、检查文件是否存在(有效,但突然停机可能有问题) 3、使用JNI(有效,但丧失可移植性)...
  • huangng
  • huangng
  • 2017年07月02日 21:09
  • 148

让程序只运行一个实例(Delphi篇)

Windows 下一个典型的特征就是多任务,我们可以同时打开多个窗口进行操作,也可以同时运行程序的多个实例,比如可以打开许多个资源管理器进行文件的移动复制操作。但有时出于某种考虑(比如安全性),我们要...
  • chaijunkun
  • chaijunkun
  • 2010年05月10日 11:46
  • 1840
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Delphi中如何防止运行一个应用程序的多个实例
举报原因:
原因补充:

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