Make Single Instance in C#

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

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
 
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() */
 
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
 
Alex Maghen said:
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.
 
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...)
 
Chris Mullins said:
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...
 
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.
 
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


Linda Liu said:
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.
 
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


Linda Liu said:
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.
 
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 + " ";
}
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 + " ";
}
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
 
Back
Top