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.
In your Form_Load:
If App.PrevInstance Then
AppActivate Me.Caption
Unload Me
Exit Sub
End If
If App.PrevInstance Then
AppActivate Me.Caption
Unload Me
Exit Sub
End If
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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("S elect * 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
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("S
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.
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(PrevHn dl)
ActiveatePrevInstance = True
End Function
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"
'Check for no success.
If PrevHndl = 0 Then
'Attempt to get window handle using VB5 class name.
PrevHndl = FindWindow("ThunderRT5Main
End If
'Check if found
If PrevHndl = 0 Then
'Attempt to get window handle using VB6 class name
PrevHndl = FindWindow("ThunderRT6Main
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(PrevHn
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)
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.
So you will not be able to make your application run, in this case.
Msgbox "The app is already running"
end if