1

I rescued this VB6-stylized snippet with intention to refactor/update but then I discovered it is not working as expected.

The code is supposed to easy determine at any time whether there is more than one instance of the same process running:

Public Declare Function CreateMutexA Lib "Kernel32.dll" (
        ByVal lpSecurityAttributes As Integer,
        ByVal bInitialOwner As Boolean,
        ByVal lpName As String) As Integer

Public Declare Function GetLastError Lib "Kernel32.dll" () As Integer

Public Function My_Application_Is_Already_Running() As Boolean

    ' Attempt to create defualt mutex owned by process
    CreateMutexA(0, True, Process.GetCurrentProcess().MainModule.ModuleName.ToString)
    Return (GetLastError() = 183) ' 183 = ERROR_ALREADY_EXISTS

End Function

If I have only one instance running then it returns False, and if I launch two instances of the same process then the function returns True as expected, the problem is that when one of those instances are closed/gone, the function stills return True.

How I could fix the code above or do this using a proper approach?.

My intention is to have a suitable helper function safe to be called at any moment from whatever app, don't hardcoding a semaphore/mutex algorithm that will work only for one specific project under some specific circunstances etc... the function should be an abstraction to avoid doing anything more than calling the function to get a result, just like the function above (wrongly)does.


Update

I have tried this:

Public NotInheritable Class AppUtil

        Private Shared created As Boolean = False
        Private Shared waitHandle As New EventWaitHandle(initialState:=False,
                                                         mode:=EventResetMode.ManualReset,
                                                         name:="AppUtil#IsRunningAnyOtherInstance",
                                                         createdNew:=created)

        Public Shared Function IsRunningAnyOtherInstance() As Boolean

                Return Not created

        End Function

End Class

In the event-handler of a button in a Form, I have this call:

MsgBox(AppUtil.IsRunningAnyOtherInstance)

After I compile the solution, I launch two instances of the app, the first instance returns False and the newer instance returns True. Both should return True because there are 2 instances open and when closing one of the forms it should return False (because there is only remaining one instance open).

PS: Take into account that the AppUtil class is not instanceable.

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • If you want a single instance vb application you can open the project properties, Application tab, and check the "Make single instance application" checkbox. – JerryM Nov 25 '15 at 22:01
  • After a quick test, it seems to work as expected. Are you sure that no additional instances are running? Bear in mind that closing the (window of a) program doesn't necessarily imply that the process is terminated. If you want to keep track of opened windows, you would have to rely on a different approach. – varocarbas Nov 25 '15 at 22:21
  • @JerryM Thanks for comment, I'm aware of the "Make single instance application" option on the project settings, but the function above is to include it in a helper dll, then the process that has called the helper function should determine if there is more than one instance running, just that. – ElektroStudios Nov 25 '15 at 22:28
  • @varocarbas Thanks for comment too, long time no see you. I'm sure no additional instances are running, I'll explain how to reproduce the error (at least on my side): 1. Create a project with a form to call the function from a button or wherever. 2. Compile the project then run two instances of the compiled assembly. 3. in form1, call the function (it will return True), in form2 call the function (it will return True), close any of the forms/process and re-call the function on the other form, it still return True. – ElektroStudios Nov 25 '15 at 22:32
  • 1
    (yes, same thing. I took a long pause, but have been around for some months already) It is quite curious because I have emulated your conditions (with a button and a `MessageBox`) and doesn't work. Previously, I was calling the function/`MessageBox` from the `Load` event of the main form and, in this way, it does work! The main difference is that, in this second case, the two opened windows have to be closed for the last check; and this (= closing all the opened windows) is what avoids the weird behaviour. Now it is a bit late; tomorrow will take a new look at it (if there is no solution yet). – varocarbas Nov 25 '15 at 22:46

3 Answers3

1

It's a lot easier nowadays.

This returns the number of processes called "svchost" to the console - you can figure the reset out eh? :0)

Please note that the name you search for has no extension. Also - It returns the results of all processes from all users.

Console.WriteLine(Process.GetProcessesByName("svchost").Count)

David Wilson
  • 4,369
  • 3
  • 18
  • 31
  • Thanks for answer but take into account that an executable file can be copied then renamed so multiple instances of the same app with different filename could be running and in that case the solution you exposed will not be efficient as a Mutex is. – ElektroStudios Nov 25 '15 at 23:44
  • Most of the time you can run multiple instances without renaming the exe tho. I'm not sure why people would want to rename the exe - I'm not posting for the sake of arguing btw - I'm actually curious why people would actually do that when the PID uniquely identifies the program anyway. – David Wilson Nov 26 '15 at 00:37
  • 1
    @ElektroStudios As far as you didn't selected any answer, I went ahead and did some tests. David's approach is the best one (I was about to write my own answer, but it is too similar to this one). The problem is that David is hardcoding the name of the executable, what is not required (why not getting the name at runtime?). PS: I didn't commented it yesterday (was a bit tired already), but do recall now this kind of problems with mutex and application not closing all its windows (I remember that there was a bunch of people some years ago using mutex for everything, but was problematic). – varocarbas Nov 26 '15 at 15:04
  • Just in case, what I mean is: `Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName)` – varocarbas Nov 26 '15 at 15:08
  • @varocarbas But retrieving the current process name at runtime doesn't solve the problem that I exposed above in the comment of this answer. It would be great to do it in a simple way like doing a simplified call to Process Class to query the processes count with "X" name but is not possible in this meaning because a .exe file can be copied, renamed, and executed, then any additiona instances of the same process wich are renamed cannot be matched this way. Thankyou both anyways! – ElektroStudios Nov 26 '15 at 16:41
  • 1
    @ElektroStudios I am not sure if I am understanding your point. But the process name is basically the file name. As said this approach works perfectly: it will keep track of all the files being executed with the same name (= only way to recognise a process/application; unless over-complicating things by relying on GUIs/registry). For example: you have file.exe, copy it three times and execute the three copies and this approach would know it. Now if you change the name of one of the copies to file2.exe, this approach wouldn't detect it (but also anything else in the computer). – varocarbas Nov 26 '15 at 16:44
  • (I've deleted the offtopic" spanish comments) I appreciate your suggestion, but think that I'm the only one that (internally from a member of a general-purpose API) will set a sort of identifier (eg. a Mutex) that will not be scoped to the user, then the user has nothing to do in that meaning, he can't change that id that should make "identificable" multiple instances of the same process whatever filenames they have. I really would like to achieve it. – ElektroStudios Nov 26 '15 at 17:10
  • 1
    @ElektroStudios As said, there are many ways to achieve that, but you would have to take care of it by your own. For example: force the application to be identified to connect to the identification source (e.g., Windows registry, remote/local database or even a file) and to update it with its current name; then the application in charge of knowing the running instances might quicky check them via `GetProcessesByName`. But all this is outside your original question. The answer to "how do I recognise all the running instances?" is what David (+ my correction) has answered. – varocarbas Nov 26 '15 at 17:19
  • 1
    @varocarbas I agree- it would be a simple matter for the program to get it's pid and add it to a file. When the program closes, remove its own entry from the file. – David Wilson Nov 27 '15 at 09:56
1

There are many ways of achieving this, in C# you could use a Mutex, Semaphore or even Windows Events, something like:

private static EventWaitHandle _handle;

bool created ;
_handle = new EventWaitHandle (false, EventResetMode.ManualReset, "my program#startup", out created) ;
if (created)
{
    StartApp();
}
else 
{
    Exit();
}

More info HERE.

Another approach would be something like:

  string procName = Process.GetCurrentProcess().ProcessName;
  if (Process.GetProcessesByName(procName).Length == 1)
  {
      // do whatever //
  }
Community
  • 1
  • 1
MaYaN
  • 6,683
  • 12
  • 57
  • 109
  • Thanks but the **EventWaitHandle** seems to implies hardcoding at first view. It could be adapted to a function that performs all the necessary logic by itself?, Imagine that you want to compile a dll that performs useful tasks, and want to include an helper function lets say `IsRunningAnyOtherInstanceOfThisProcess() As Boolean`, just that abstract function that will automate the necessary things to determine the result, with no manually **EventWaitHandle** declaration/usage from the user side. – ElektroStudios Nov 26 '15 at 00:39
  • It's just a matter of changing the above to return a `bool` instead of `StratApp()` and `Exit()` – MaYaN Nov 26 '15 at 07:34
  • Its not working as expected for me, please see my question edit. – ElektroStudios Nov 26 '15 at 12:42
1

Easiest way in my opinion is by using the Mutex class:

        bool isFirstInstance = false;
        Mutex mutex = new Mutex(true, "MyUniqueAppKeyOrGuid", out isFirstInstance);

The first instance will have variable isFirstInstance set to true - all next instances will have it set to false.

gd73
  • 635
  • 6
  • 21
  • Thanks but when the first instance is closed, the remaining instance still return False. I think that is the hard part to achieve because the mutex seems cannot be released. – ElektroStudios Nov 26 '15 at 13:08
  • I must have misunderstood the question. In that case you should use a memory mapped file, as outlined here: https://msdn.microsoft.com/en-us/library/dd997372(v=vs.110).aspx sample code is available. "Non-persisted files are memory-mapped files that are not associated with a file on a disk. When the last process has finished working with the file, the data is lost and the file is reclaimed by garbage collection. " – gd73 Nov 26 '15 at 14:12