现在的位置: 首页 > 综合 > 正文

只运行一个实例,且试图运行第二个实例时自动激活第一个实例。

2013年08月27日 ⁄ 综合 ⁄ 共 3596字 ⁄ 字号 评论关闭

       很多程序象winamp,TTplayer,RealPlayer等都有自动激活前一个实例的功能,其实这玩意也很简单的,在VB中可以用App.hInstance 来判断是否运行了程序的一个实例,用API函数FindWindow来找到前一个实例的句柄,从而为激活她做好了准备。但是App.hInstance 有很大的局限性,一个不需要其他资源支持的EXE文件,如果你复制到其他目录,这个复制品就可以畅通无阻的运行而不管他的前辈是否已运行,如果这个程序需要独占某个硬件或其他资源,这样就会存在潜在的冲突,因此我们必须寻找其他可靠的途径来保证我们作品的排一无二性。

       Windows中互斥体的概念使得上面的问题得以轻松解决。我们在程序第一次运行得时候创建一个特定的互斥体,在第二次或者更多次运运行时程序在创建互斥体时就会发生错误,我们捕捉到这个错误并加以适当得处理就得以保证程序运行得唯一性。

       那如何激活前一个实例呢,别着急,Windows的API函数就是丰富,我们只要有了程序的句柄(就相当于人们的身份证一样,系统会保证他的唯一性),就可以对他进行随意的操作,ShowWindow,SetForeGround,SetActiveWindow等等。如何得到这个ID呢,FindWindow,不错,很多人会这么说。的确,这是个方法,并且是个不错的函数,但是她的程序标题的敏感性使得很多情况下我们必须抛弃她。比如winmap她的标题会随着播放歌曲的改变而变化。怎么办,不急,既然句柄是唯一的,我们为何不在程序启动后把她记录在我们的硬盘上呢。

       好了,前面的分析使得我们在理论上能够实现类似于winamp的效果了,那为何不添加点新东西:在我们激活第一个窗体时,我们让她来回闪烁几下不是更能够得到用户的注意吗?FlashWindow的效果,对,就是FlashWindow,仔细看看她的使用方法,要配合一个定时器来使用,哎,实现这个小功能,还要用定时器,真是有点不划算啊,并且在sub main中如何使用定时器也值得一番研究。那有没有简单得方法,of course, 看看FlashWindowEx把,Ex扩展的函数就是不一样,她有一个结构参数可以指定闪烁的次数和每次闪烁的时间。COOL!

      说了一堆,还的付诸于实践,代码,我要的是代码,别急,就来了。

模块中:

'程序实现功能:自动激活前一个实例
'作    者: laviewpbt
'联系方式:
laviewpbt@sina.com
'QQ:     33184777
'版本:Version 1.3.0
'说明:复制请保留源作者信息,转载请说明,欢迎大家提出意见和建议

Public Declare Function ReleaseMutex Lib "kernel32" (ByVal hMutex As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA" (lpMutexAttributes As SECURITY_ATTRIBUTES, ByVal bInitialOwner As Long, ByVal lpName As String) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function FlashWindowEx Lib "user32.dll" (ByRef pfwi As FLASHWINFO) As Long

Private Const FLASHW_STOP = 0 '停止闪烁,系统恢复窗体到她原始的状态.
Private Const FLASHW_CAPTION = &H1 '闪烁窗体的标题
Private Const FLASHW_TRAY = &H2 '闪烁任务栏
Private Const FLASHW_ALL = (FLASHW_CAPTION Or FLASHW_TRAY) '标题栏&窗体标题一起闪烁
Private Const FLASHW_TIMER = &H4 '连续的闪烁,直到设置了FLASHW_STOP标志

Private Const ERROR_ALREADY_EXISTS = 183&
Private Const SW_RESTORE = 9
Private Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As Long
        bInheritHandle As Long
End Type

Private Type FLASHWINFO
    cbSize As Long   '结构大小
    hwnd As Long
    dwFlags As Long
    uCount As Long   '闪烁的次数
    dwTimeout As Long  '闪烁的时间
End Type

Public Mutex As Long

Private Sub Main()
    Dim sa As SECURITY_ATTRIBUTES
    Dim hwnd As Long
    Dim FlashInfo As FLASHWINFO

    sa.bInheritHandle = 1
    sa.lpSecurityDescriptor = 0
    sa.nLength = Len(sa)
    Mutex = CreateMutex(sa, 1, App.Title) '试着创建一个新的互斥体
    If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then        '互斥体已经存在,表明已打开一个实例
        hwnd = GetSetting("AppName", "Section", "Key", 0)    '找到我们保存的前一个实例的句柄,这样避免了使用FindWindow函数等对变标题程序的无奈
        ShowWindow hwnd, SW_RESTORE         '显示窗口
        SetForegroundWindow hwnd            '激活窗体
       
        FlashInfo.cbSize = Len(FlashInfo)
        FlashInfo.dwFlags = FLASHW_ALL Or FLASHW_TIMER
        FlashInfo.dwTimeout = 0       '以毫秒为单位指定窗体闪烁的速率,如果为0,则使用默认的光标闪烁速率.
        FlashInfo.hwnd = hwnd
        FlashInfo.uCount = 3        '指定闪烁的次数.
        FlashWindowEx FlashInfo     '闪烁效果
    Else
        FrmMain.Show                        '显示主窗体
    End If
End Sub

窗体中:

Private Sub Form_Load()
    SaveSetting "AppName", "Section", "Key", Me.hwnd   '临时保存我们的窗体句柄
End Sub

Private Sub Form_Unload(Cancel As Integer)
    ReleaseMutex Mutex                                  '释放互斥体,没有这两句,你在VB环境下这可以运行一次,再次按F5的时候你会发现自动退出
    CloseHandle Mutex
    DeleteSetting "AppName", "Section", "Key"           '清除我们在注册表种存放的数据,绿色效果。
End Sub

     注意,启动窗体情选择sub main()

     代码中的注释已相当的详细,我想稍微懂VB的人应该都不难看懂了吧。

     稍微提一下,用DDE的方式也可以实现上述程序的功能,网上有这方面的文章,大家有兴趣可以自己去看看。

     All Rights Reserved !

 

       

抱歉!评论已关闭.