(转载)
对于有些应用程序,只希望在系统中运行它的一个实例,也就是在程序运行期间,禁止它
第二次运行。
为了实现上述功能,可以采用下面的方法:在主窗体初始化的时候,设置窗体的类名。应
用程序在运行的最开始,判断当前系统中是否存在该窗体类名。如果存在,则给已经运行的应
用程序发送一个还原或在前台显示的消息,并且退出;如果不存在,则继续执行应用程序。
例程S16_32 该例程对上述功能进行了说明。
首先在主窗体中自定义一个消息,并为主窗体的类名定义一个字符串常量。为了设置窗体
的类名,需要重载T C u s t o m F o r m 类的方法C r e a t e P a r a m s 。在U n i t 1 . p a s 中添加代码如下:
i n t e r f a c e
u s e s
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
c o n s t
C M _ R E S TORE = WM_USER + $1000; // 自定义一个消息,用来恢复窗口
APPNAME = 'S16_33'; // 窗体类名字符串常量
t y p e
TForm1 = class(TForm)
. . .
p u b l i c
procedure CreateParams(var Params: TCreateParams); override;
Procedure RestoreRequest(var message: TMessage); message CM_RESTO R E ;
e n d ;
. . .
i m p l e m e n t a t i o n
{$R *. D F M }
{ TForm1 }
procedure TForm1.CreateParams(var Params: TCreateParams);
b e g i n
// 设置窗体的类名
inherited CreateParams(Params);
P a r a m s . WinClassName := APPNAME;
e n d ;
下面添加测试窗体类名的代码。
为了在程序运行的一开始就测试当前系统中是否存在主窗体中要定义的类名,可以在工程
文件中添加代码进行测试。测试前需要定义与主窗体中一致的自定义消息和窗体类名字符串常
量。工程文件中的有关代码如下:
program Project1;
u s e s
F o r m s , M e s s a g e s , Wi n d o w s ,
Unit1 in 'Unit1.pas' {Form1};
c o n s t
// 自定义一个消息,用来恢复窗口
C M _ R E S TORE = WM_USER + $1000;
APPNAME = 'S16_33'; // 窗体类名字符串常量
v a r
下载
RvHandle : hWnd; // 窗口句柄
{$R *. R E S }
b e g i n
RvHandle := FindWindow(APPNAME, NIL); // 根据窗体类名查找窗口句柄
if RvHandle > 0 then // 如果找到则发送自定义的消息并退出
b e g i n
PostMessage(RvHandle, CM_RESTORE, 0, 0);
E x i t ;
e n d ;
A p p l i c a t i o n . I n i t i a l i z e ;
Application.CreateForm(TForm1, Form1);
A p p l i c a t i o n . R u n ;
e n d .
最后将在主窗体中添加接收自定义消息的代码。
在U n i t 1 . p a s 文件中T F o r m 1 的定义中添加接收自定义消息的过程R e s t o r e R e q u e s t ,并在类的
定义体中按下C t r l + S h i f t + C ,完善过程的定义体。具体代码如下:
t y p e
TForm1 = class(TForm)
. . .
p u b l i c
procedure CreateParams(var Params: TCreateParams); override;
Procedure RestoreRequest(var message: TMessage); message CM_RESTO R E ;
e n d ;
. . .
procedure TForm1.RestoreRequest(var message: TMessage);
b e g i n
// 接到自定义的消息后,如果处于最小化状态则恢复,否则放置到桌面的最前面
M e s s a g e B o x ( H a n d l e , '程序" ' + A P P N A M E + ' "已经运行了。' ,
'信息' , M B _ O K + M B _ I C O N I N F O R M AT I O N + M B _ S Y S T E M M O D A L ) ;
if IsIconic(Application.Handle) = TRUE then
A p p l i c a t i o n . R e s t o r e
e l s e
A p p l i c a t i o n . B r i n g To F r o n t ;
e n d ;
程序编译、链接通过后,可以在Wi n d o w s 的资源管理器中运行测试。

解决方案 »

  1.   

    楼上的方法太复杂了,我有一个稍微简单的方法:用OS提供的同步对象来实现。1·可在Project Source的program关键字的begin语句之后、三个Application语句之前创建一个命名的同步对象(如Event,Mutex,Semaphore等均可,注意:所取的名字对于某一个程序来说必须是唯一的,这样才能保证下一个实例试图创建命名相同的同步对象时只能得到第一个实例已创建的同步对象的句柄);2·然后用WaitForSingleObject测试该同步对象是否处于有信号态,有则退出;否则3·如果为无信号态,则将其设置为有信号态,并运行那三个Application语句。
      

  2.   

    倘若你不熟悉互斥对象的建立,可以用如下的方法:
    使用当前窗口Handle,来寻找同层级最顶上的窗口
           h:=FindWindow(Form1.Handle,HWND_FIRST)
         然后一个个用HWND_NEXT找下来,用GetWindowText读出标题,比较和您的程序
    标题吻合的个数,超过一个,就将Handle不等于自己的窗口设为Focus,中止当前程序。
    注意:用GetWindowText必须先验证其Parent是NULL,即此窗体是主窗体。这样好些,免得你的程序如果有同名窗口的话
      

  3.   

    这是一种简单的方法.
    原理同上,代码如下:
    加到工程文件中.
    const classname='你的主窗口类名';//尽量不要是容易重复的名字.
    var
      handle:integer;
    begin  
      handle:=findwindow(classname,nil);
      if handle<>0 then
      begin
        halt;
      end;
      Application.Initialize;
      Application.CreateForm(....);
      Application.Run;
    end;
      

  4.   

    1、对主窗口程序的改动:在主窗口(即程序创建的第一个窗口)中interface节加入
    const
    CM_RESTORE = WM_USER + $1000; {自定义的“恢复”消息}
    MYAPPNAME = "My Delphi Program";
    并在Form的定义的public节中加入
    procedure CreateParams(var Params: TCreateParams); override;
    Procedure RestoreRequest(var message: TMessage); message CM_RESTORE;
    在implementation节中加入
    {指定窗口名称}
    procedure TForm1.CreateParams(var Params: TCreateParams);
    begin
    inherited CreateParams(Params);
    Params.WinClassName := MYAPPNAME;
    end;{处理“恢复”消息}
    procedure TForm1.RestoreRequest(var message: TMessage);
    begin
    if IsIconic(Application.Handle) = TRUE then
    Application.Restore
    else
    Application.BringToFront;
    end;经过以上修改,程序的主窗口的类名已经被指定了,这是进行判断的基础。一般在程序刚开始运行的时候进行判断,所以还要对DPR文件进行修改。2、对DPR文件的改动在 uses 节中添加 windows、messages这两个单元加入下列语句,注意两个文件中常量CM_RESTORE和MYAPPNAME的定义必须一致
    const
    CM_RESTORE = WM_USER + $1000; {自定义的“恢复”消息}
    MYAPPNAME = "My Delphi Program";
    var
    RvHandle : hWnd;将下列语句插到程序最前部(在Application.Initialize之前)RvHandle := FindWindow(MYAPPNAME, NIL);
    if RvHandle > 0 then
    begin
    PostMessage(RvHandle, CM_RESTORE, 0, 0);
    Exit;
    end;
    这段程序的意思是如果找到一个类名相同的窗口,则向该窗口发送一个消息,并退出,而本例中原窗口收到该消息后会自动激活或从图标还原,从而达到了避免二次运行且能自动调出前一例程的目的。