只要在工程文件中作下面的处理就搞定了
var
hmutex:hwnd;
ret:integer;
begin
Application.Initialize;
hmutex:=createmutex(nil,false,'project1');
ret:=getlasterror;
if ret<>error_already_exists then
begin
Application.CreateForm(TForm1, Form1);
end
else
begin
messagedlg('程序已运行。',mtinformation,[mbok],0);
releasemutex(hmutex);
end;
var
hmutex:hwnd;
ret:integer;
begin
Application.Initialize;
hmutex:=createmutex(nil,false,'project1');
ret:=getlasterror;
if ret<>error_already_exists then
begin
Application.CreateForm(TForm1, Form1);
end
else
begin
messagedlg('程序已运行。',mtinformation,[mbok],0);
releasemutex(hmutex);
end;
实现单实例运行的关键是判断前一实例是否存在,Win3.x中运行的程序能获知前一实
例的句柄,从而可以方便地进行判断,但 Windows 95 是抢先式多任务系统,其程序
的前一实例句柄恒为零,所以只有另寻其他办法。
据说有很多方法都可解决这个问题,不过我只知道两种,下面分别加以介绍。★第一种方法:
通过查看是否有相同窗口类名的例程存在来进行判断。API函数 FindWindow 按照指定类名和窗口名来找到窗口的句柄,该函数并不查找子窗口。FindWindow的函数原型为:HWND FindWindow(LPCTSTR lpClassName, // address of class name
LPCTSTR lpWindowName // address of window name
); 参数:lpClassName 欲查找窗口的类名。该变量的类型为以零结尾的字符串,也就是C/C++中
的字符串类型。
lpWindowName 窗口名。变量类型同上。返回值:若成功,返回指定窗口的句柄;否则返回NULL。如果希望得到更多的失败信息,可以调用
GetLastError 函数。例:var
PrevWindow:HWND; //定义一个句柄变量begin
Application.Initialize; //程序初始化
PrevWindow:=FindWindow('TForm1','运行时实例测试');
//窗口的类名为'TForm1',窗口名为'运行时实例测试'
//在Delphi中,窗口的类名可以从程序的TYPE区域里看到,而窗口名就是窗口的Caption属性。
if PrevWindow<>0 then //如果找到此窗口,说明程序已经运行了一个实例
Begin
MessageBox(0,"已经运行了该程序!","警告",MB_ICONWARNING+MB_OK);
Application.Terminate; //程序终止运行以保证只有一个实例
end;
Application.CreateForm(TForm1, Form1); //否则程序继续运行
Application.Run;
end.//以上程序在Delphi 3.0 中通过。FindWindow函数的详细用法参见Win32 Api Help。WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
HWND PreWindows;
try
{
Application->Initialize();
PreWindows=FindWindow("TForm1","运行实例测试");
if(PreWindows)
{
MessageBox(0,"已经运行了该程序!","警告",MB_ICONWARNING+MB_OK);
Application->Terminate();
}
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
//以上程序在C++ Builder 1.0 中通过。★第二种方法:
第二种方法就是利用API函数CreateMutex。CreateMutex函数用来创建一个已命名或是未命名的互斥体。CreateMutex的函数原型为:HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, // address of security attributes
BOOL bInitialOwner, // flag for initial ownership
LPCTSTR lpName // address of mutex-object name
); 参数说明:lpMutexAttributes 此参数指向一个SECURITY_ATTRIBUTES结构,此结构指定该互斥体的安全
属性,如果该参数为NULL(即为空),则以缺省的安全描述符来创建互斥
体,并且该函数返回的句柄不能被继承。
(此参数好象不好理解,不过在此处你只需将其设为NULL就行了!)bInitialOwner 指定互斥体对象的初始拥有者。在此处设为False。lpName 此参数指定互斥体的名字。参数类型是以NULL结尾的字符串。返回值:如果函数成功则返回互斥体的句柄,如果指定的互斥体名已经存在,则GetLastError函数返回
ERROR_ALREADY_EXISTS。例:
program DEL3test;uses
Forms,Windows,SysUtils,
DEL3unit in 'DEL3unit.pas' {Form1};{$R *.RES}Var
hMutex:HWND;
Ret:Integer;
begin
Application.Initialize;
Application.Title := 'aaaaaa';
hMutex:=CreateMutex(nil,False,'aaaaaa');
Ret:=GetLastError;
If Ret<>ERROR_ALREADY_EXISTS Then
Begin
Application.CreateForm(TForm1, Form1);
Application.Run;
End
Else
Application.MessageBox('Run Twice!','Notes!',MB_OK);
ReleaseMutex(hMutex);
end.//以上例子在Delphi 3.0中通过。说明:这两种方法一般来说并没有什么很大的区别,也没有在什么地方看到有关这两者的说明。
不过我在自己的程序中正好分别试过这两种方法,发现了这两者还是有一个区别。
如果程序中有一个启动画面,如Visual Foxpro,Delphi,wps97等等,当采用第一种方法
时,仍然会出现启动画面,尽管在程序中我是先判断有无实例存在,然后再显示启动画面。
为什么会这样,我也不清楚,而用第二种方法就不会出现这种情况,所以我一直都用第二
种方法。
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;
这段程序的意思是如果找到一个类名相同的窗口,则向该窗口发送一个消息,并退 出,而本例中原窗口收到该消息后会自动激活或从图标还原,从而达到了避免二次运行.