Visual Basic 6's App object exposes a useful property named PrevInstance, which allows you to determine whether there are other instances of the same application running. This property has never been implemented in VB.NET, even though a VB2005 application can use the StartupNextInstance event for this purpose.
' to display this code, open the Application page of the My Project designer and click the Application Events button
The problem of this approach is that the event is raised in the first instance of the application, not in the new instance, therefore when the VB.NET application starts you can't determine for sure whether it is the only instance in the system. In other words, you can learn only whether and when another application is launched, not if the current applicatin is the first and only running instance.
.NET developers have solved this problem in a variety of ways, for example using Mutexes or by iterating over the collection returned by the Process.GetProcesses method. In .NET we have a new and more elegant alternative, based on the System.Threading.Semaphore type. A semaphore is a counter which can be incremented and decremented. If its value becomes zero, a thread can't decrement it and must wait for another thread to increment it to a value >0. Interestingly, if the semaphore has a name it becomes a system-wid object which can be shared by all the applications that ask for a reference to a semaphore with that specific name. It is therefore sufficient that the app creates a semaphore with a unique name just after its launch (e.g. it can be a name that includes the executable's path) to have all the instances of a given app share the same semaphore and therefore the same counter. The only problem left to solve is ensure that the semaphore's counter be correctly resotred when the application terminates, but this is easy to achieve by means of a Finalize method.
In addition to the PrevInstance property - which returns False if the application was the only running instance when the application was launched - the following VB6App class exposes also the InstanceCount property, which returns the total number of instances in that moment (the current app is included in the count). Here's the VB2005 version of this class:
Class
' the system-wide semaphore Private semaphore As System.Threading.Semaphore ' initial count for the semaphore (very high value) Private Const MAXCOUNT As Integer = 10000
Private Sub New() Dim ownership As Boolean = False ' create a unique name, but strip invalid characters Dim name As String = "VB6App_" & System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(":", "").Replace("\", "") semaphore = New System.Threading.Semaphore(MAXCOUNT, MAXCOUNT, name, ownership) ' decrement its value semaphore.WaitOne() ' if we got ownership, this app has no previous instances m_PrevInstance = Not ownership End Sub
' the PrevInstance property returns True if there was a previous instance running ' when the default instance was created Private Shared m_PrevInstance As Boolean
Public Shared ReadOnly Property PrevInstance() As Boolean Get Return m_PrevInstance End Get End Property
' return the total number of instances of the same application that are currently running Public Shared ReadOnly Property InstanceCount() As Integer Get ' release the semaphore and grab the previous count Dim prevCount As Integer = DefValue.semaphore.Release() ' acquire the semaphore again DefValue.semaphore.WaitOne() ' eval the number of other instances that are currently running Return MAXCOUNT - prevCount End Get End Property
Protected Overrides Sub Finalize() ' increment the semaphore when the application terminates semaphore.Release() End Sub
End Class
Notice that this class has a Finalize method without implementing IDisposable. It is one of those special cases when it is OK to violate the Dispose-Finalize pattern.
To understand how the class works, just keep in mind that the Semaphore.Release method increments the internal counter, whereas the WaitOne method decrements it. You must test the VB6App.PrevInstance property as soon as the application starts, for example in the Sub Main method or in the Load event hander for the main form, as to let the VB6App class store the boolean value internally. The same form might then test the value of the InstanceCount property on exit, for example if you need to run some cleanup code when the last application instance is about to terminate:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not VB6App.PrevInstance Then ' open the common log file ' ... End IfEnd Sub
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing If VB6App.InstanceCount = 1 Then ' close the common log file. ' ... End IfEnd Sub
Here's the C# version of the class. (I fixed the original version to fix two syntax errors mentioned in one of the comments below.)
public class VB6App { // the default instance private static VB6App DefValue = new VB6App(); // the system-wide semaphore private System.Threading.Semaphore semaphore; // initial count for the semaphore (very high value) private const int MAXCOUNT = 10000;
private VB6App() { // create a named (system-wide semaphore) bool ownership = false; // create the semaphore or get a reference to an existing semaphore
string name = "VB6App_" + System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(":", "").Replace("\\", ""); semaphore = new System.Threading.Semaphore( MAXCOUNT, MAXCOUNT, name, out ownership); // decrement its value semaphore.WaitOne(); // if we got ownership, this app has no previous instances m_PrevInstance = !ownership; }
// the PrevInstance property returns True if there was a previous instance running // when the default instance was created
private static bool m_PrevInstance ;
public static bool PrevInstance { get { return m_PrevInstance ; } }
// return the total number of instances of the same application that are currently running
public static int InstanceCount { get { // release the semaphore and grab the previous count int prevCount = DefValue.semaphore.Release(); // acquire the semaphore again DefValue.semaphore.WaitOne(); // eval the number of other instances that are currently running return MAXCOUNT - prevCount; } }
~VB6App() { // increment the semaphore when the application terminates semaphore.Release(); } }
Remember Me
Powered by: newtelligence dasBlog 1.8.5223.1