Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1257941
  • 博文数量: 788
  • 博客积分: 4000
  • 博客等级: 上校
  • 技术积分: 7005
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-19 15:52
文章存档

2017年(81)

2011年(1)

2009年(369)

2008年(337)

分类:

2009-02-03 09:56:12



如何避免一个程序同时运行两个或多个实例?

参考:  
   
  使用互斥对象让程序只运行一次  
  “怎么让我的程序在运行时不能重复打开?”经常在论坛上看到有朋友问这方面的问题。本文将比较详细的说明这一问题,并给出一个较为完善的解决方案。  
   
  尽管这已经不是一个新问题了,但这里还是简要的说明一下这种技术:这的确是一个相当有用的技术,可能你经常会注意到相当多的程序在运行之后当你再次点击运行时,它只是会回到原来的窗口,而不会运行两个程序。就如同你在运行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开发者指南》  
 

program   Project1;  
   
  uses  
      Forms,   Windows,  
      Unit1   in   'Unit1.pas'   {Form1};  
   
  var  
      hMutex:THandle;  
   
  {$R   *.res}  
   
  begin  
      hMutex:=OpenMutex(MUTEX_ALL_ACCESS,true,'TestMutex');  
      if   hMutex<>0   then  
      begin  
          Application.Terminate;  
      end;  
      Application.Initialize;  
      CreateMutex(MUTEX_ALL_ACCESS,false,'TestMutex');  
      Application.CreateForm(TForm1,   Form1);  
      Application.Run;  
  end.

上面的代码修改一下,加一个Exit;  
  hMutex:=OpenMutex(MUTEX_ALL_ACCESS,true,'TestMutex');  
      if   hMutex<>0   then  
      begin  
          Application.Terminate;  
          Exit;  
      end;  
      Application.Initialize;  
      CreateMutex(nil,false,'TestMutex');  
      Application.CreateForm(TForm1,   Form1);  
      Application.Run;



--------------------------
新闻:阿里巴巴宣布今年将新招聘5000人
导航:博客园首页  知识库  新闻  招聘  社区  小组  博问  网摘  找找看
阅读(388) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~