2

I've written the next code that prevents multi-instancing via MUTEX, but I would like to extend its funcionality by allowing to set the maximum number of allowed instances, so I should could decide whether I want to allow only a single instance or a multi-instace of only 3 instances (for example).

I've tried to figure out how to count the amount of mutexes of a process, but I didn't find anything about.

By the other hand I've found a lot of posts in StackOverflow explaining how to count the amount of processes by it's filename, I'm not interested in any of this 'cause I've choosed a MUTEX detection which for me is more secure.

Someone could help me in this whim?.

This is all what I have done:

Namespace My

    Partial Friend Class MyApplication

#Region " Properties "

        ''' <summary>
        ''' Gets the current process mutex identifier.
        ''' </summary>
        ''' <value>The current process mutex identifier.</value>
        ''' <exception cref="System.FormatException">The specified value is not a valid GUID format.</exception>
        Private ReadOnly Property MutexID As String
            Get
                ' Define a Golabl Unique Identifier to name the Mutex.
                Dim Id As String = "b045ce40-2863-4ce7-a7df-8afca8214454"

                If Guid.TryParse(input:=Id, result:=New Guid) Then
                    Return Id
                Else
                    Throw New FormatException("The specified value is not in a valid GUID format.")
                End If

            End Get
        End Property

        ''' <summary>
        ''' Gets the maximum instances allowed for this process.
        ''' </summary>
        ''' <value>The maximum instances allowed for this process.</value>
        Private ReadOnly Property MaxInstances As Integer
            Get
                Return 2
            End Get
        End Property

#End Region

#Region " Private Methods "

        ''' <summary>
        ''' Determines whether this is the unique instance that is running for this process.
        ''' </summary>
        ''' <returns><c>true</c> if this is the unique instance; otherwise, <c>false</c>.</returns>
        Private Function IsUniqueInstance() As Boolean

            Dim mtx As Threading.Mutex = Nothing

            Try
                mtx = Threading.Mutex.OpenExisting(name:=Me.MutexID)
                mtx.Close()
                mtx = Nothing
            Catch
                mtx = New Threading.Mutex(initiallyOwned:=True, name:=Me.MutexID)
            End Try

            Return mtx IsNot Nothing

        End Function

#End Region

#Region " Event-Handlers "

        ''' <summary>
        ''' This occurs when the application starts, before the startup Form is created.
        ''' </summary>
        ''' <param name="sender">The source of the event.</param>
        ''' <param name="e">The <see cref="ApplicationServices.StartupEventArgs"/> instance containing the event data.</param>
        Private Sub MyApplication_Startup(ByVal sender As Object, ByVal e As ApplicationServices.StartupEventArgs) _
        Handles Me.Startup

            ' If there is more than one instance running of this process with the same mutex then...
            If Not Me.IsUniqueInstance Then ' Prevent multi-instancing.

                MessageBox.Show("This is a limited demo, to run multiple instances please purchase the program.",
                               Application.Info.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error)

                ' Cancel the application execution.
                e.Cancel = True

            End If

        End Sub

#End Region

    End Class ' MyApplication

End Namespace

UPDATE

This is the code that I'm trying, I've tried it in the application events, in the form constructor, in the load and shown event, but the multiinstances are not detected.

    Private Sub test() Handles Me.Startup

        Using semaphore As New Semaphore(3I, 3I, "something")

            If Not semaphore.WaitOne(100I) Then
                MsgBox("Already max instances running")
                End
            Else
                MsgBox("test")
            End If

        End Using

    End Sub
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • You need a `Semaphore` instead of `Mutex` – Sriram Sakthivel Aug 18 '14 at 06:52
  • What do you see you get `Already max instances running` messagebox? – Sriram Sakthivel Aug 18 '14 at 10:38
  • No, I always get the "test" message – ElektroStudios Aug 18 '14 at 10:39
  • I understand the problem. You should remove that using statement. You need to make the `semaphore` variable as instance variable rather than local variable. And `Dispose` it when your application ends. I used `using` statement because it is the `Main` method. Then this should work. – Sriram Sakthivel Aug 18 '14 at 10:42
  • 1
    No it will not have any relationship to the `Semaphore` unless your threads uses this semaphore instance and that calls `semaphore.WaitOne`. Your semaphore instance should be untouched by any other threads. Also note that you shouldn't call `test` method more than once. Calling this method more than once will give the impression that there are multiple processes running even when they're not. – Sriram Sakthivel Aug 18 '14 at 10:55

1 Answers1

4

Mutex is for mutual exclusion. It will atmost allow only one thread to proceed. If you need to allow number of threads(process in this case) to proceed you need a Semaphore rather than mutex.

C# sample:

private static void Main()
{
    using (Semaphore semaphore = new Semaphore(3, 3, Assembly.GetExecutingAssembly().GetName().FullName))
    {
        if (!semaphore.WaitOne(100))
        {
            MessageBox.Show("Already max instances running");
            return;
        }

        //Start your application here
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

VB.Net sample:

Private Shared Sub Main()
    Using semaphore As New Semaphore(3, 3, Assembly.GetExecutingAssembly().GetName().FullName)
        If Not semaphore.WaitOne(100) Then
            MessageBox.Show("Already max instances running")
            Return
        End If

       'Start your application here
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(New Form1())
    End Using
End Sub

P.S: Code converted with http://converter.telerik.com/ from C# to VB

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • Thanks but the code that you've provided works only in a CLI app, the same code didn't worked in a GUI app it does not detected multi-instances, also you are using the assembly name but I'm not sure if that means that the detection is via the name of the app (which can be renamed by the user and I've specified that I prefer to avoid those kind of techniques) or is just a secure ID like for a MUTEX, also another question is if this will go in conflict with the threads of my app, 'cause my intention is to limit process instances but not threads on multi-threaded apps, thanks for your answer – ElektroStudios Aug 18 '14 at 09:04
  • @ElektroStudios This is a generic answer should work for all the applications. I tried with GUI application(Winforms) and it works. Application name cannot be changed by user(You may think of renaming the process file but assembly name cannot be renamed). I'm checking assembly name, so that's not a problem. You can change this code to use `GUID` if you like. and also there is nothing to limit the number of process, all you have is limit the number of threads, using that we're terminating the process. – Sriram Sakthivel Aug 18 '14 at 09:22
  • Am 100% sure, this is what you need. If you have trouble implementing this, you may need to post the code what you tried. I'll look into it. – Sriram Sakthivel Aug 18 '14 at 09:23
  • @ Sriram Sakthivel in a VB.NET empty project, I've just copied the using block that you've provided into the form constructor, also I've tried it into the "Load" and "Shown" ebent-handler but anything worked, could you upload the source of that gui app to know how you've implemented the semaphore?, thanks for your help. – ElektroStudios Aug 18 '14 at 10:12
  • 1
    You shouldn't use it in constructor. You should use it in `Main` method. If you're using in constructor instead of `Return` you should use `Environment.Exit(0)` – Sriram Sakthivel Aug 18 '14 at 10:13
  • If I use a limit of 3 instances in the semaphore ctor and I run the same app 3 times then I close two instances so I have only 1 running then I run another instance and it gives a false positive (it is detected as a 4th instance), so something I'm doing wrong, I've solved it by releasing the semaphore on the FormClose event, but I don't know if I could do it all in the application events without messing more code. – ElektroStudios Aug 18 '14 at 11:19
  • Also I would like to detect the current instance index (to say something) to launch an specific message if it's the first instance, another message if is the second, and another if is the third instance, whoa, I see that I need to post a new question hehe, thanks a lot for all!. EDIT: I've done that part on the 'ShutDown' event – ElektroStudios Aug 18 '14 at 11:22