|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThis article will attempt to show how to setup a VB.NET Windows application to become a Single Instance type application within the .NET Framework. Techniques used include Mutexes, .NET Remoting and Thread communications between Forms. Please note that I did not separate the functionality into separate classes to keep the code size down. Also note that I did not utilize any BackgroundRecent work on a project presented a problem where we needed the main application to allow only one instance of the application to run at a time. It was also required that secondary instances that were started should pass the command line arguments to the running application to be processed. Using the codeTo use this code in your VB.NET application, simply add the SingleInstanceHandler.vb file to your project and add the following code to your startup code: Module ModMain
Private m_Handler As New SingleInstanceHandler()
Public Sub Main(ByVal args() As String)
'Add the startup call back
AddHandler m_Handler.StartUpEvent, AddressOf StartUp
m_Handler.Run(args)
End Sub
Public Sub StartUp(ByVal sender As Object,
ByVal event_args As StartUpEventArgs)
End Sub
End Module
The For my example project, I have a MDI form Private m_MainForm As SingleInstanceForm
Public Sub StartUp(ByVal sender As Object, ByVal event_args As StartUpEventArgs)
' This is the first instance, create the main form
' and addd the child forms
If event_args.NewInstance Then
m_MainForm = New SingleInstanceForm()
m_MainForm.AddChildForms(event_args.Args)
Application.Run(m_MainForm)
Else
' This is coming from another instance, so simple add the child forms
' We are probably calling from a seperate thread, so we need to use
' the forms Invoke command to ensure that we are perform the
' AddForm method in the proper thread.
Dim parameters() As Object = {event_args.Args}
m_MainForm.Invoke(CType(AddressOf m_MainForm.AddChildForms,
SingleInstanceForm.AddChildFormsDelegate), parameters)
End If
End Sub
The Private m_Mutex As System.Threading.Mutex
Private m_UniqueIdentifier As String
Private m_TCPChannel As System.Runtime.Remoting.Channels.Tcp.TcpChannel
Public Sub Run(ByVal args() As String)
' Create a Unique Identifier for the Mutex object and the
'SingletonCommunicator. We are using the executable path for the
'application because we want to ensure that the instances are unique
'based on installation locations.
'Hack: Attempting to create a Mutex with an identifier that looks like a
'path
' will cause a path not found exception. So simply replace the '\'s with
' underscores and this should still be a unique identifier for this app
' based on the running location.
m_UniqueIdentifier = Application.ExecutablePath.Replace("\", "_")
' Create the Mutex class
m_Mutex = New System.Threading.Mutex(False, m_UniqueIdentifier)
' Attempt to lock the Mutex
' We locked it! We are the first instance!!!
If m_Mutex.WaitOne(1, True) Then
CreateInstanceChannel()
' Raise the StartUpEvent as a NewInstance
Dim event_args As New StartUpEventArgs(True, args)
RaiseStartUpEvent(event_args)
Else
' Not the first instance!!!
' Raise the StartUpEvent through the
' SingletonCommunicator not as a NewInstance
Dim event_args As New StartUpEventArgs(False, args)
UseInstanceChannel(event_args)
End If
End Sub
Please note that the The The Private m_TCPChannel As System.Runtime.Remoting.Channels.Tcp.TcpChannel
Private Sub CreateInstanceChannel()
System.Runtime.Remoting.RemotingServices.Marshal(Me, m_UniqueIdentifier)
Dim tcp_properties As IDictionary = New Hashtable(2)
tcp_properties("bindTo") = "127.0.0.1"
tcp_properties("port") = 0
m_TCPChannel = New System.Runtime.Remoting.Channels.Tcp.TcpChannel( _
tcp_properties, Nothing, Nothing)
System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel( _
m_TCPChannel)
Dim key As Microsoft.Win32.RegistryKey = Application.UserAppDataRegistry()
key.SetValue(m_UniqueIdentifier,
m_TCPChannel.GetUrlsForUri(m_UniqueIdentifier)(0))
End Sub
The Private Sub UseInstanceChannel(ByVal event_args As StartUpEventArgs)
Dim key As Microsoft.Win32.RegistryKey = Application.UserAppDataRegistry()
Dim remote_component As SingleInstanceHandler = CType( _
System.Runtime.Remoting.RemotingServices.Connect(_
GetType(SingleInstanceHandler), _
key.GetValue(m_UniqueIdentifier)), SingleInstanceHandler)
remote_component.RaiseStartUpEvent(event_args)
End Sub
The Public Sub RaiseStartUpEvent(ByVal event_args As StartUpEventArgs)
RaiseEvent StartUpEvent(Me, event_args)
End Sub
Public Event StartUpEvent As StartUpEventArgsHandler
The <Serializable()> _
Public Class StartUpEventArgs
Inherits EventArgs
Public Sub New(ByVal new_instance As Boolean, ByVal the_args() As String)
NewInstance = new_instance
Args = the_args
End Sub
Public NewInstance As Boolean
Public Args() As String
End Class
Points of InterestOne of the interesting errors that I ran across when developing this solution was an exception that occurred when the application tried to create additional child forms from a second instance; the following exception would occur: Using the ' We are probably calling from a seperate thread, so we need to use the forms
' Invoke command to ensure that we are perform the AddChildForms method in
' the proper thread.
Dim parameters() As Object = {event_args.Args}
m_MainForm.Invoke(CType(AddressOf m_MainForm.AddChildForms, _
SingleInstanceForm.AddChildFormsDelegate), parameters)
HistoryMarch 12, 2003 - Initial release of the article. | ||||||||||||||||||||