首 页 | 新 闻 | 技术中心 | 第二书店 | 《程序员》 | 《开发高手》 | 社 区 | 黄 页 | 人 才
移 动专 题SUNIBM微 软微 创精 华Donews人 邮
我的技术中心 
我的分类 我的文档
全部文章 发表文章
专栏管理 使用说明



 RSS 订阅 
最新文档列表
Windows/.NET
.NET  (rss)    
Visual C++  (rss)    
Delphi  (rss)    
Visual Basic  (rss)    
ASP  (rss)    
JavaScript  (rss)    
Java/Linux
Java  (rss)    
Perl  (rss)    
综合
其他开发语言  (rss)    
文件格式  (rss)    
企业开发
游戏开发  (rss)    
网站制作技术  (rss)    
数据库
数据库开发  (rss)    
软件工程
其他  (rss)    

积极原创作者 
goodboy1881 (13)
wangchinaking (58)
iiprogram (67)
fancyhf (1)
harrymeng (41)
yjz0065 (113)
coofucoo (105)
Drate (69)
lphpc (30)
smallnest (61)
CSDN - 文档中心 - Delphi 阅读:7615   评论: 11    参与评论
标题   使用互斥对象让程序只运行一次     选择自 hkbarton 的 Blog
关键字   Delphi,Win32
出处  

使用互斥对象让程序只运行一次

“怎么让我的程序在运行时不能重复打开?”经常在论坛上看到有朋友问这方面的问题。本文将比较详细的说明这一问题,并给出一个较为完善的解决方案。

尽管这已经不是一个新问题了,但这里还是简要的说明一下这种技术:这的确是一个相当有用的技术,可能你经常会注意到相当多的程序在运行之后当你再次点击运行时,它只是会回到原来的窗口,而不会运行两个程序。就如同你在运行delphi时,在外部点开另一个工程文件时,delphi只是会简单的将你的当前工程置换而不是运行两个delphi。这样的好处是显而易见的:你不必担心你的程序在某些情况下被别的软件恶意运行多次而吃光内存造成当机。下面我们做进一部的说明:

熟悉win32编程的朋友(特别是多线程编程),相信对互斥对象已经相当熟悉了,它常被用做线程间同步的技术手段。这里我们使用它来防止程序重复运行。我们只是简要的提一下互斥对象,并不做深入研究:互斥对象把第一次建立它的程序作为主程序,这样我们只用检测互斥对象是否已经有主程序就判断程序是否已经运行过,这里需要涉及到一个api函数:WaitForSingleObject该函数的第一个参数为用以检测的互斥对象,第2个参数的表示函数返回结果前的滞留时间,如果改函数返回wait_TimeOut就表明互斥对象已经有了一个主程序。修改了的工程文件代码如下:(注意:以下的代码都出现在工程文件中,而不是单元文件中,并且这里都在最简单的delphi默认建立的工程基础上修改)

var

 myMutex:HWND;

begin

  myMutex:=CreateMutex(nil,false,'hkOneCopy');// CreateMutex建立互斥对象,并且给互斥对象起一个唯一的名字。

  if WaitForSingleObject(myMutex,0)<>wait_TimeOut then//程序没有被运行过

  begin

   Application.Initialize;

   Application.CreateForm(TForm1, Form1);

   Application.Run;

  End;

End;

下面的工作是来完善这个程序,我们不仅希望程序可以不被重复运行,而且我们也希望当用户再次点击程序可执行文件时,已经运行的程序能够做出一些响应。在这里我们希望它能够变为最上层的活动窗口以提醒用户程序已经被运行。为了达到这个目的,我们必须先获得已经运行程序的窗口句柄,以便使用SetForeGroundWindow(handle)来使程序窗口最前并激活。为了得到这个句柄,我们必须使用windows枚举函数EnumWindows来遍历windows的窗口列表,该函数可以使用一个回调函数作为参数,并用这个回调函数来对每一个系统中的窗口进行调用直到最后一个窗口或回调函数返回false为止,这个回调函数规定有两个参数(handle,Cardinal,只用注意第一个handle参数它表示由枚举函数当前遍历到的窗口句柄)。我们只要编写这个函数并在其中不断的比较当前遍历到的窗口类名和我们的程序的主窗口类名,以及比较窗口可执行文件的名称和我们程序的名称直到找到相同的为止,将这时的窗口句柄保存下来就可以了,下面的代码加上了适当的注释:

function EnumWndProc(hwnd:Thandle;param:Cardinal):bool;stdcall;

//由于用于api回调函数,请使用windows传统的参数传递方式stdcall

var

 ClassName,WinMoudleName:string;

 WinInstance:THandle;

begin

 result:=true;

 SetLength(ClassName,100);

 GetClassName(hwnd,pchar(ClassName),length(ClassName));//获得当前遍历窗口的类名

 ClassName:=pchar(ClassName);//在字符串后加结束符,确定字符串结束

 if ClassName=TForm1.ClassName then//比较

 begin

  WinInstance:=GetWindowLong(hwnd,GWL_HINSTANCE);//获得当前遍历窗口的实例

  setlength(WinMoudleName,100);

  GetModuleFileName(WinInstance,pchar(WinMoudleName),length(WinMoudleName));

  //获得当前遍历窗口的程序文件名

  WinMoudleName:=pchar(WinMoudleName);

  if WinMoudleName=MoudleName then//MoudleName为工程全局变量,自身程序的文件名

  begin

   FindHid:=hwnd;//FindHid为工程全局变量保存找到的句炳

   result:=false;//找到以后就结束遍历

  end;

 end;

end;

下面是全部的工程文件:

var

 hMutex,FindHid:HWND;

 MoudleName:string;

begin

  hMutex:=CreateMutex(nil,false,'hkOneCopy');

  if WaitForSingleObject(hMutex,0)<>wait_TimeOut then

  begin

   ……//略去的代码在前文

  end

  else

  begin

   SetLength(MoudleName,100);

   GetModuleFileName(HInstance,pchar(MoudleName),length(MoudleName));

   //获得自己程序文件名

   MoudleName:=pchar(MoudleName);

   EnumWindows(@EnumWndProc,0);//调用枚举函数

   if FindHid<>0 then

    SetForegroundWindow(FindHid);

  end;

end.

为了使我们的程序更完美,让它能在重复运行的时候展现更多的特性(如delphi中的置换工程文件为当前打开的工程),你还可以向找到的窗口句柄发送用户消息,再在窗口的消息处理函数中做相应的处理,你一定可以让我们的程序更眩!

参考文献:

   delphi开发者指南》


相关文章
对该文的评论
retardate ( 2004-02-11)
如下使用,为何不行?

调用函数,

if ShowDllForm(Application.Handle,Hmutex,TTestPlugin(PlugIns[TmenuItem(sender).Tag]).Caption)=1 then
    ShowMessage('打开窗体错误');

Dll接口函数
procedure ShowDllForm(AHandle,aHmutex:THandle;ACaption:string);stdcall;
var
  Dll_Form:TFrmPlugins;
  i:integer;
begin
  //hMutex:=CreateMutex(nil,false,'hkOneCopy');
  i:=WaitForSingleObject(ahMutex,0);
  showmessage(inttostr(i));
  if i<>wait_TimeOut then
  begin
    Application.Handle:=aHandle;
    Dll_form:=TFrmPlugins.Create(Application);
    Dll_form.Caption:=ACaption;
    Dll_Form.Show;
  end else
  begin
    SetLength(MoudleName,100);
    GetModuleFileName(Hinstance,pchar(MoudleName),length(MoudleName));
    //获得自己程序文件名
    MoudleName:=pchar(MoudleName);
    EnumWindows(@EnumWndProc,0);//调用枚举函数
    if FindHid<>0 then
      SetForegroundWindow(FindHid);
  end;
  //closeHandle(hmutex);
end;
retardate ( 2004-02-11)
请问楼主,窗体如果在DLL包中如何控制,在调用函数中在加一个Hmutex参数,在Dll窗体中调用 WaitForSingleObject(ahMutex,0) 然后在进行控制性么?为什么我这样做不行?,,请楼主指教
hkbarton ( 2003-09-24)
他们讲了书上的知识,还敢自称原创,真是过分啊
hkbarton ( 2003-09-24)
关于delphi的文章,市面上多入牛毛的书早就讲了几遍了,关于编程的书加起来可以讲解现在几乎100%的技术问题,那这些文章怎么能自称原创呢???
kindguy ( 2003-09-23)
shit shit richer早就在windows核心编程里写过了,竟然敢自称原创