Link to home
Start Free TrialLog in
Avatar of deming
deming

asked on

VB6: How to prevent multiple instances of same application from loading?

I have written a VB6 exe and want to know how to prevent multiple copies of the exe from running at the same time on the same computer.  I would like to make it so that if the user tries to run a second copy, it simply sets the focus to the copy already running.

Avatar of bobbit31
bobbit31
Flag of United States of America image

if App.PrevInstance then
    Msgbox "The app is already running"
end if
Avatar of Xentor_
Xentor_

In your Form_Load:

If App.PrevInstance Then
  AppActivate Me.Caption
  Unload Me
  Exit Sub
End If
ASKER CERTIFIED SOLUTION
Avatar of xassets
xassets

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Or

Private Sub Form_Load()
    On Error Resume Next
    If App.PrevInstance Then
        AppActivate FindProcess(App.EXEName & ".exe")
        End
    End If
End Sub

Private Function FindProcess(Process) As Long
    Dim objWMIService, colProcesses, objProcess
   
   Set objWMIService = GetObject("winmgmts:")
   Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process")
   For Each objProcess In colProcesses
       If LCase(Process) = LCase(objProcess.Caption) Then
            FindProcess = objProcess.processid
            Exit For
        End If
   Next
End Function
App.PrevInstance can't prevent starting your application from different location (different path or different name).
WMI can't prevent the same.

In start-up object of your project (Form name or Sub_Main or whatever it is), you must create some global object (like mutex or pipe) and name it with unique name. If creating such object fails, then it already exists (another instance of your app created it already) so you will know that other instance of your app (even in different location) is still running.
Hm, answer does not prevent runnig your app twice (from different locations or different EXE names).
I like the mutex idea but its not well established in VB which would concern me. If the app gets "end tasked" can you guarantee that the mutex will be destroyed ? Or would you get a "thread terminated while holding a mutex" error ? - you wouldn't want that.

In fact the user has no control over app.title, even if they rename the EXE and put it in a different directory, so if you call my solution without checking app.previnstance (just exclude the first IF, and call ActivateInstance unconditionally in sub main), then we have a solution which will stop multiple instances regardless of the app exename or path.

One more point about my solution. The "End" statement is a bit ungraceful. You'd probably want to exit gracefully as follows:

Sub Main()
    If ActivatePrevInstance() = True then
        exit sub
    end if

    frmMain.Show
    etc etc

End Sub


In a module

Option Explicit

Public Const GW_HWNDPREV = 3

Declare Function OpenIcon Lib "user32" (ByVal hwnd As Long) As Long
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
   (ByVal lpClassName As String, ByVal lpWindowName As String) _
   As Long
Declare Function GetWindow Lib "user32" _
   (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Declare Function SetForegroundWindow Lib "user32" _
   (ByVal hwnd As Long) As Long
   
Public Function ActivatePrevInstance() as boolean
   Dim OldTitle As String
   Dim PrevHndl As Long
   Dim result As Long

   'Save the title of the application.
   OldTitle = App.Title

   'Rename the title of this application so FindWindow
   'will not find this application instance.
   App.Title = "unwanted instance"

   'Attempt to get window handle using VB4 class name.
   PrevHndl = FindWindow("ThunderRTMain", OldTitle)

   'Check for no success.
   If PrevHndl = 0 Then
       'Attempt to get window handle using VB5 class name.
       PrevHndl = FindWindow("ThunderRT5Main", OldTitle)
   End If

   'Check if found
   If PrevHndl = 0 Then
       'Attempt to get window handle using VB6 class name
       PrevHndl = FindWindow("ThunderRT6Main", OldTitle)
   End If

   'Check if found
   If PrevHndl = 0 Then
       'No previous instance found.
        ActivatePrevInstance = False
       Exit Sub
   End If

   'Get handle to previous window.
   PrevHndl = GetWindow(PrevHndl, GW_HWNDPREV)

   'Restore the program.
   result = OpenIcon(PrevHndl)

   'Activate the application.
   result = SetForegroundWindow(PrevHndl)

    ActiveatePrevInstance = True
End Function

No problem with End-Task-ing my exe. If you end-task it, the mutex is no longer exist. I tried it right now. You should try it too. Here is the code:

Private Const ERROR_ALREADY_EXISTS = 183&
Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA" (lpMutexAttributes As Any, ByVal bInitialOwner As Long, ByVal lpName As String) As Long
Private Declare Function ReleaseMutex Lib "kernel32" (ByVal hMutex As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long



Private Sub Form_Load()

Dim lngMutexHandle As Long
Dim lngLastError As Long

 ' Createmutex with above "strange" name - use names, that you think no other programs will not use :)
lngMutexHandle = CreateMutex(ByVal 0&, 1, "YourUniqueProgramNameHere")
 ' Check if the mute is already created (by other instance of this program)
If Err.LastDllError = ERROR_ALREADY_EXISTS Then
    ' Release handles
    ReleaseMutex lngMutexHandle
    CloseHandle lngMutexHandle
     ' Here you can place your code when another instance is started
    MsgBox "There are another instance of this program already running."
Else
     ' No other instance is detected
     ' So do your job here
End If

End Sub




No problem with End-Task this - if first instance of EXE file is terminated, second will load normally. (I'm using XP)
My Idea would be to check for a note  someware in the Windows registry when the Application starts, if it doesn't exist create it in the form_load oder sub main procedure and delete it agin, when the user shuts down the application in Form_QueryUnload.
I tried to use the registry, but the problem is the following : if the application is stopped "in abnomal conditions" (You unplug the computer for example), the registry key wil still be there on the next start off the application, even if the applicatin is not running.

So you will not be able to make your application run, in this case.