2

App.Previnstance returns a value of True or False depending on whether a previous of the program is running when this instance starts.

Subsequently if the previous instance is terminated the value of App.PrevInstance does not change.

Is it possible to write a function that would be able to determine at any moment if previous instances are in existence?

I guess that for this you would need the date/time processes started to be available. As this information does not seem to be available from the task manager I wonder if windows stores it at all?

kjack
  • 2,004
  • 3
  • 26
  • 41
  • 1
    The cheapest way would be to use `CreateMutex` with a GUID for the mutex name. If the function fails then another instance of the application is already running. – wqw May 25 '13 at 12:28
  • @wqw I'd heard of a mutex but never knew what it was. Wish I'd known sooner! Based on this comment and a google I've posted something that works for me below – kjack May 25 '13 at 13:52

2 Answers2

2

The problem you're seeing with App.PrevInstance might be because you're testing with the application running under the debugger (i.e., the VB 6 IDE). But I'm not entirely sure. It might just perform the check once and cache the value, which allows it to grow "stale" as the environment state changes. As you've already noticed, the App.PrevInstance property has a lot of limitations. Another common problem with it is its fragility. Changing the name of the executable is an easy way to make it fail. That is not always the desired behavior.

So it's a good idea to replace it with an alternative solution. Like wqw says in a comment, the best solution would be to create a mutex using a GUID for the name of the mutex whenever your application starts up. This should succeed the first time, but will fail subsequently because the mutex is already registered (by a previous instance of your application). That failure is your clue that a previous instance of your application is running. To do this in VB 6, you will need to import and call some Win32 functions, like CreateMutex, CloseHandle, and ReleaseMutex. There's a sample of how to use mutexes on MSDN, but that won't help you very much to write VB 6 code unless you are already rather familiar with the Win32 API. I've linked to a tutorial that contains the necessary code in VB 6 in my answer here.

If you're otherwise satisfied with the behavior of App.PrevInstance and you just want it to perform the check each time you call it (rather than using a stale cached value), then you can just replace it with a call to your own function that does essentially the same thing: iterate through all of the currently-running processes, and look for a match to the name of your executable. Unfortunately, this is not necessarily less work than the "better" solution involving the use of a mutex. You'll still need to import a number of Win32 functions, including EnumProcesses. There are instructions for this in an old knowledge base article—obviously you want to focus on the "Windows NT" section and ignore the "Windows 95/98" stuff.

I guess that for this you would need the date/time processes started to be available. As this information does not seem to be available from the task manager I wonder if windows stores it at all?

You don't actually need this information. In fact, I'm not sure what approach you had in mind that would require it. It doesn't matter when the process was started, it just matters whether or not it is currently running. Those are two completely different things.

However, just for fun, Windows does in fact store this information. Task Manager doesn't show it, but Process Explorer does. You can retrieve it programmatically either by calling the GetProcessTimes function, or querying WMI (specifically, the CreationDate property of the Win32_Process class).

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • @Code Gray Thank you for this. As you you say using a mutex was the way to go. As for the time the processes started, I thought I needed this in order for one of the processes to be able to identify itself as the oldest and thus be able to takeover the task that only one instance should be doing. – kjack May 25 '13 at 14:11
  • 1
    BTW I have just confirmed that the problem with app.previnstance staying true after the previous instance has been terminated is not confined to the IDE. – kjack May 25 '13 at 14:15
2

Thanks to wqw's comment I looked up CreateMutex and it was exactly what I needed.

I found the code below here

'Code by Adam Verwijs
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 hMutex As Long
    'Try to create a new Mutex
    hMutex = CreateMutex(ByVal 0&, 1, App.Title)
    'Did the mutex already exist?
    If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then
        'Clean up
        ReleaseMutex hMutex
        CloseHandle hMutex
        'More than one instance detected
        MsgBox "More than one instance"
        End
    Else
        'form load code
    End If
End Sub

EDIT to show that same non zero mutex returned : If you create a new vb6 project with 1 button, stick the code below in, make the project and then run multiple instances you'll see that all have the same non-zero mutex, at least on my computer (windows vista home basic)

   Option Explicit

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 Command1_Click()
 Dim hMutex As Long
    'Try to create a new Mutex
    hMutex = CreateMutex(ByVal 0&, 1, App.Title)
    MsgBox hMutex
    'Did the mutex already exist?
    If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then
        'Clean up
        ReleaseMutex hMutex
        CloseHandle hMutex
        'More than one instance detected
        MsgBox "More than one instance"


    End If
End Sub

EDIT 2016-04-17 DO NOT USE THIS CODE!!! I have used it without noticing a problem until recently but have now discovered that it does not work across more than one user logged in on a computer. Use wqw's answer on this other thread instead

Community
  • 1
  • 1
kjack
  • 2,004
  • 3
  • 26
  • 41
  • In your "already exists cleanup" you are working with a NULL handle. There is nothing to release or close there. Interesting how incorrect code that "works most of the time" continues to propagate to mislead another generation. According to the docs you should check for a NULL handle return before looking at LastDllError anyway. You also assume success when any other error occurs. This logic is wrong in multiple ways. – Bob77 May 26 '13 at 00:38
  • @Bob77 A little knowledge is a dangerous thing! I've edited it out but left the code from the API docs in place. – kjack May 26 '13 at 01:36
  • 1
    You should probably check that `hMutex` is 0 (`NULL`) in addition to checking `LastDllError`. And I would use a GUID or something else guaranteed to be unique as the mutex ID, rather than just your application's title/name. That could potentially collide with some other object. Imagine if you wrote something called "Notepad"! And yes, like Bob says, if `CreateMutex` fails, there's nothing to release or clean up. The clean up code should be placed in your app's termination sequence so that it gets executed when the *first* instance closes. (Although Windows will take care of this for you.) – Cody Gray - on strike May 26 '13 at 06:03
  • @Cody Gray Thanks but I don't understand this really. I made a very simple program with one button that creates a mutex, displays its handle and has the error code above. When I run multiple versions they give the same non zero handle but they all also throw the ERROR_ALREADY_EXISTS. I never get a zero handle however many are running. – kjack May 26 '13 at 09:03
  • If `CreateMutex` fails, then `hMutex` will be 0 and `LastDllError` will indicate what the error was. It is *usually* `ERROR_ALREADY_EXISTS`, but it doesn't have to be. Something else could cause it to fail and you could get a different error like `ERROR_ACCESS_DENIED`. The only way that `hMutex` can be non-zero is if the `CreateMutex` function succeeded. So what you're saying doesn't make much sense to me. – Cody Gray - on strike May 26 '13 at 09:14
  • @Cody Gray Thank you very much for taking an interest in this. I know I am misunderstanding something but I edited my answer to put in the code that has multiple exes returning the same non zero handle to show you what I mean. Is this expected behaviour? – kjack May 26 '13 at 09:23
  • Ah, nuts. Sorry, you're entirely right. I didn't read the documentation carefully enough. Never mind on that. `CreateMutex` acts like `OpenMutex` if the mutex already exists. – Cody Gray - on strike May 26 '13 at 09:36
  • @Cody Gray, No need to apologise at all, thanks very much for helping me through this stuff. – kjack May 26 '13 at 09:44
  • @kjack: Thanks for the answer and the redirect to wqw's demo. – Laurie Stearn Apr 13 '17 at 02:59