.NET Forum / .NET Framework / New Users / April 2007
Make Single Instance in C#
|
|
Thread rating:  |
Alex Maghen - 11 Apr 2007 21:24 GMT In the Visual Studio documentation for VISUAL BASIC ONLY, there seems to be the ability to mark an application as "Single Instance" so that it will run only once. How do implement this same functionality in a Windows Forms C# application?
Alex
Jim Rand - 11 Apr 2007 22:05 GMT internal void ProcessImport(bool IsInteractive, ProcessMode processMode, System.ComponentModel.ISynchronizeInvoke synchronizer) { _IsInteractive = IsInteractive;
/* Use mutex to allow only one running instance of application */ bool gotMutex = false; if (_mutex == null) { _mutex = new Mutex(true, "MLXListingImport", out _ownsMutex); if (_ownsMutex)
{ gotMutex = true; } }
/* Didn't get it */ if (!gotMutex) { _log.Warn("Attempted to start second running instance of program."); if (IsInteractive) { MessageBox.Show( "Import processing is currently running in an other instance of this program.", "Import Request Cancelled",MessageBoxButtons.OK,MessageBoxIcon.Information); } OnImportComplete(); }
} /* ProcessImport() */
> In the Visual Studio documentation for VISUAL BASIC ONLY, there seems to > be [quoted text clipped - 4 lines] > > Alex Alex Maghen - 11 Apr 2007 22:26 GMT Jim -
Thanks for this and forgive but... I'm not quite sure what I would do with this. What do I do with the "internal void ProcessImport()" function? Where does it go? Do I call it from somewhere?
Alex
> internal void ProcessImport(bool IsInteractive, ProcessMode processMode, > System.ComponentModel.ISynchronizeInvoke [quoted text clipped - 39 lines] > > > > Alex Jon Skeet [C# MVP] - 11 Apr 2007 22:34 GMT > Thanks for this and forgive but... I'm not quite sure what I would do with > this. What do I do with the "internal void ProcessImport()" function? Where > does it go? Do I call it from somewhere? The example was a bit confusing IMO, as it didn't show the other variables involved etc.
Have a look at http://pobox.com/~skeet/csharp/faq/#one.application.instance and see if that helps.
You'll want to insert the sample code (using an appropriate unique name) early on, eg as the first thing Main does.
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet If replying to the group, please do not mail me too
Chris Mullins [MVP] - 12 Apr 2007 01:04 GMT It's funny - I just today had to do this for an application that we offer. There was one additional key - if the application was already started, the new instance should cause the old instance to Activate, then the new instance should just disappear. This means (to the user) that if they launch the application, they get to use the application.
I ran into a surprisingly ugly scenario, where the application was minimized to the system tray (not the task bar), and the Process class would return "0" as the Main Window Handle for the process. This made ShowWindow() very unhappy.
I found code to work around this at: http://www.myjavaserver.com/~walthari/SpecialServices.cs
His implemention of FindMainWindow(int processId) makes use of EnumWindows in a way I wouldn't have thought of - and more importantly, it worked. Everything worked great after that.
(I didn't use any of his other code, as I rolled my own implementation using a Named Mutex. This is easy enough to do, and I have some weird custom requirements based on user settings and configuration options...)
 Signature Chris Mullins, MCSD.NET, MCPD:Enterprise, Microsoft C# MVP http://www.coversant.com/blogs/cmullins
>> Thanks for this and forgive but... I'm not quite sure what I would do >> with [quoted text clipped - 11 lines] > You'll want to insert the sample code (using an appropriate unique > name) early on, eg as the first thing Main does. Jon Skeet [C# MVP] - 12 Apr 2007 07:55 GMT > It's funny - I just today had to do this for an application that we offer. > There was one additional key - if the application was already started, the > new instance should cause the old instance to Activate, then the new > instance should just disappear. This means (to the user) that if they launch > the application, they get to use the application. <snip>
Yup, that's a pretty common scenario. Time to update the FAQ to include that link, I suspect...
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet If replying to the group, please do not mail me too
Jim Rand - 12 Apr 2007 02:08 GMT This was just a quick example of using a Mutex to stop a second instance from running.
Linda Liu [MSFT] - 12 Apr 2007 10:22 GMT Hi Alex,
Yes, a WinForms application written in VB.NEThave a specific application framework, with which we could config a VB.NET application a single instance application. Unfortunately, a WinForms application written in C# doesn't have such a application framework.
A common way to make a application written in C# single instance is to make use of Mutex, as Jon has suggested. You may add the following code to the static Main method of the application.
using System.Threading;
[STAThread] static void Main() { bool firstInstance; Mutex mutex = new Mutex(false, "Local\\" +someUniqueName, out firstInstance);
if (firstInstance) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } else { MessageBox.Show("An instance has already been run!"); } }
If you're using VS05, you have another option, that is to use Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase class. We could derive a class from WindowsFormsApplicationBase class and set the IsSingleInstance property to true.
The following is a sample. It requires that you add a reference to 'Microsoft.VisualBasic' to your C# project.
using System.Windows.Forms; using Microsoft.VisualBasic.ApplicationServices;
class Program:WindowsFormsApplicationBase { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Program prog = new Program(); prog.EnableVisualStyles = true; prog.MainForm = new Form1(); prog.Run(new string[] { ""});
} public Program() { this.IsSingleInstance = true; }
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs) { MessageBox.Show("another instance has been run!"); base.OnStartupNextInstance(eventArgs); } }
Hope this helps. If you have anything unclear, please feel free to let me know.
Sincerely, Linda Liu Microsoft Online Community Support
================================================== Get notification to my posts through email? Please refer to http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif ications. Note: The MSDN Managed Newsgroup support offering is for non-urgent issues where an initial response from the community or a Microsoft Support Engineer within 1 business day is acceptable. Please note that each follow up response may take approximately 2 business days as the support professional working with you may need further investigation to reach the most efficient resolution. The offering is not appropriate for situations that require urgent, real-time or phone-based interactions or complex project analysis and dump analysis issues. Issues of this nature are best handled working with a dedicated Microsoft Support Engineer by contacting Microsoft Customer Support Services (CSS) at http://msdn.microsoft.com/subscriptions/support/default.aspx. ================================================== This posting is provided "AS IS" with no warranties, and confers no rights.
Alex Maghen - 12 Apr 2007 14:00 GMT Linda -
This is very helpful. Just one question about your C# sample (the top one): In the case where it's NOT the first instance, how do I get access to the information about how this attempt at running the app ocurred (things like command line parameters)?
Alex
> Hi Alex, > [quoted text clipped - 92 lines] > > This posting is provided "AS IS" with no warranties, and confers no rights. Alex Maghen - 12 Apr 2007 23:42 GMT Linda -
One more question about this - What happens when the application is run for a second time and, instead of running it that second time, in my Main() I want to be able to call a function inside the main form of the original instance. How do I get a reference to that Form object here?
Alex
> Hi Alex, > [quoted text clipped - 92 lines] > > This posting is provided "AS IS" with no warranties, and confers no rights. Linda Liu [MSFT] - 16 Apr 2007 06:53 GMT Hi Alex,
Thank you for your reply.
As for the option of using mutex, we could use ChannelServices to register a channel for the main form when the application is run for the first time.
When the application is run for the second time, we could create a proxy for the previous instance using RemotingServices. Pass the command line parameters of the second instance or call a function inside the main form of the original instance, and then exit the second instance.
The following is a sample. It requires that you add a reference to System.Runtime.Remoting in your application.
using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Threading;
static class Program { [STAThread] static void Main(string[] args) { bool firstInstance = false; string safeName = Application.UserAppDataPath.Replace(@"\", "_"); Mutex mutex = new Mutex(true, safeName, out firstInstance); if(!firstInstance) { string formUrl = "tcp://localhost:1313/form1"; Form1 otherMainfrm = (Form1)RemotingServices.Connect(typeof(Form1), formUrl); otherMainfrm.Method1(args); return; } ChannelServices.RegisterChannel(new TcpChannel(1313),false); RemotingServices.Marshal(form1, "form1"); Application.Run(form1); }
static Form1 form1 = new Form1(); }
public partial class Form1 : Form { public Form1() { InitializeComponent(); } delegate void MethodCallback(string[] args);
public void Method1(string[] args) { // Note that it is not allowed for non-UI thread to access controls on the form, instead, we should use the Invoke method of the form to execute a delegate on the UI thread that own's the control's underlying windows handle. if(this.InvokeRequired) { MethodCallback callback = new MethodCallback(Method1); this.Invoke(callback, new object[] { args }); return; } string command = ""; for(int i =0;i<args.Length; i++) { command += args[i] + " "; } this.textBox1.Text = command; } }
As the option of using WindowsFormsApplicationBase, it's easy and straight forward to do this. The following is a sample.
using System.Windows.Forms; using Microsoft.VisualBasic.ApplicationServices;
class Program:WindowsFormsApplicationBase { [STAThread] static void Main(string[] commandline) { Program prog = new Program(); mainform = new Form1(); prog.MainForm = mainform; prog.Run(commandline); } static Form1 mainform = null; public Program() { this.IsSingleInstance = true; } protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs) { string command = ""; for(int i =0 ; i<eventArgs.CommandLine.Count; i++) { command += eventArgs.CommandLine[i] + " "; } MessageBox.Show("this is the second instance! " + eventArgs.CommandLine.Count.ToString() + " " + command); base.OnStartupNextInstance(eventArgs); mainform.Method1(); } }
Hope this helps. If you have any question, please feel free to let me know.
Sincerely, Linda Liu Microsoft Online Community Support
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|