|
Introduction
Enforcing a rule that only one instance of process is running is an interesting task.
There are many ways to code the algorithm on Win32. I solved the problem by creating a reusable
C# class that encapsulates one of the quirks of the Win32 API and makes the identification of
another "same" process more certain.
This article was triggered (and borrows heavily from) another article
written by Marc Clifton, "Detect if another process is running and bring it to the foreground" which
is also available on CodeProject. When implementing Marc's code I ran into a few issues:
- The code was not in an easily reusable format.
- Identifying processes by name alone can be error prone when the name is common.
- The multi-threaded nature of Win32 could conceivably have an undesirable effect on
the reporting of an initial "same" process instance.
- Long assembly names trigger partially hidden process names in Win32.
Solution
In order to enhance the detection of other instances of a process, I decided to use a
named mutex synchronization object. By definition, Win32 dictates that only one instance of
a named mutex will exist on a system and any given time. Win32 further guarantees that only
one thread will own the mutex at any given time. Since a mutex can cross the process boundary,
it became a good choice for identifying existing processes.
I encapsulated the mutex in a class (SingleProgramInstance) and had the
constructors attempt to create and gain ownership of the mutex immediately. Failure to
gain ownership indicates the existence of another "same" process currently running. If the
object successfully acquires ownership the program will hold onto it during its operation and
therefore, announce to the system that it is the initial one and only process.
A property (IsSingleInstance) is used to indicate this state.
There are two constructors for the object. One of the constructors accepts no
parameters and uses the current assembly name to name the mutex. Because assembly names
can be common between different applications, I created a second constructor that accepts a string parameter.
The object appends this string to the assembly name to help differentiate it from other applications.
The programmer can pass in a few meaningless characters to greatly reduce the likelihood of name duplication.
UI Concerns
When another "same" process is identified, it is usually nice to present the
previous instance to the user before terminating the redundant process. I separated
this code from the detection code because it may not be desirable in all circumstances (i.e. tray icon applications).
Essentially, I ask Windows for the all of the processes that match the current process name and use
some Win32 Interop calls to restore and bring the process to the foreground. Herein lies a small quirk with Win32.
Only the first 15 characters of a process name are made available when you ask for a process name.
Unfortunately, when you ask for all of the processes of a certain name (via Process.GetProcessesByName),
Windows uses the whole name (which may exceed 15 characters) when performing the test.
That leaves us the choice of looping through all the processes ourselves and testing only the
first 15 characters or find out our full process name and ask windows to return only those that match.
Since the first 15 characters may not be unique enough for a good test, I choose the second approach.
Luckily, it seems that the assembly name and the full (hidden) process name are one and the same.
Using the assembly name, Win32 returns only the processes that match the full process name and I can
easily ignore my own process by testing the process id.
Cleanup
Since this object is reliant on a system resource (mutex) a good method for
clean up should be employed. I chose to use the IDisposable interface
to guarantee a deterministic release of the mutex object. When used properly, the
mutex will be released immediately upon program termination so that an additional process
can again be started. This should happen anyway due to the nature of a mutex but that is
no excuse for sloppy code. Below is a example of the proper use of this object. Notice
that the using statement neatly wraps the Application.Run().
using SpecialServices;
....
[STAThread]
static void Main()
{
using(SingleProgramInstance spi = new SingleProgramInstance("x5k6yz"))
{
if (spi.IsSingleInstance)
{
Application.Run(new Form1());
}
else
{
spi.RaiseOtherProcess();
}
}
}
....
Object Source
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;
namespace SpecialServices
{
public class SingleProgramInstance : IDisposable
{
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd,int nCmdShow);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
private const int SW_RESTORE = 9;
private Mutex _processSync;
private bool _owned = false;
public SingleProgramInstance()
{
_processSync = new Mutex(
true,
Assembly.GetExecutingAssembly().GetName().Name,
out _owned);
}
public SingleProgramInstance(string identifier)
{
_processSync = new Mutex(
true,
Assembly.GetExecutingAssembly().GetName().Name + identifier,
out _owned);
}
~SingleProgramInstance()
{
Release();
}
public bool IsSingleInstance
{
get {return _owned;}
}
public void RaiseOtherProcess()
{
Process proc = Process.GetCurrentProcess();
string assemblyName =
Assembly.GetExecutingAssembly().GetName().Name;
foreach (Process otherProc in
Process.GetProcessesByName(assemblyName))
{
if (proc.Id != otherProc.Id)
{
IntPtr hWnd = otherProc.MainWindowHandle;
if (IsIconic(hWnd))
{
ShowWindowAsync(hWnd,SW_RESTORE);
}
SetForegroundWindow(hWnd);
break;
}
}
}
private void Release()
{
if (_owned)
{
_processSync.ReleaseMutex();
_owned = false;
}
}
#region Implementation of IDisposable
public void Dispose()
{
Release();
GC.SuppressFinalize(this);
}
#endregion
}
}
Summary
I would like to thank Marc Clifton for his insightful article that drove me to create the
above solution. Hopefully, others will expand upon what we have done to produce an even more solid solution.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 55 (Total in Forum: 55) (Refresh) | FirstPrevNext |
|
|
 |
|
|
Hi, I used this code for may application but this class in a DLL file. When I run my application on Windows Vista, it have error
"Application has generated an exception that could not be handled
Process id=0x694, Thread id=...."
Why??? Pls help me!
========================== Nothing to change my life
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
As it stands, your code works as is. However, if you move the SingleProgramInstance class into a DLL and then have your EXE use the SingleProgramInstance class in the DLL, there is a bug. If you launch an instance of an app which uses SingleProgramInstance, minimize it, then launch another instance, with the (current) following code:
public void RaiseOtherProcess() { Process proc = Process.GetCurrentProcess(); // ... string assemblyName = Assembly.GetExecutingAssembly().GetName().Name; foreach (Process otherProc in Process.GetProcessesByName(assemblyName)) { ...
...the call to Process.GetCurrentProcess() returns back the process of the DLL, not the EXE. My fix is to change RaiseOtherProcess() to:
public void RaiseOtherProcess(Process proc, Assembly executingAssembly) { if(proc == null) { throw new ArgumentException("proc"); }
if(executingAssembly == null) { throw new ArgumentException("executingAssembly"); }
// Using Process.ProcessName does not function properly when // the name exceeds 15 characters. Using the assembly name // takes care of this problem and is more accurate than other // work arounds. string assemblyName = executingAssembly.GetName().Name; foreach (Process otherProc in Process.GetProcessesByName(assemblyName)) { //ignore this process if (proc.Id != otherProc.Id) { // Found a "same named process". // Assume it is the one we want brought to the foreground. // Use the Win32 API to bring it to the foreground. IntPtr hWnd = otherProc.MainWindowHandle; if (IsIconic(hWnd)) { ShowWindowAsync(hWnd,SW_RESTORE); } SetForegroundWindow(hWnd); return; } } }
...and change the code which calls RaiseOtherProcess() to:
spi.RaiseOtherProcess(System.Diagnostics.Process.GetCurrentProcess(), System.Reflection.Assembly.GetExecutingAssembly());
Jeff
-- modified at 16:43 Thursday 15th March, 2007
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
PS. I really hate how CodeProject removes whitespace. I originally submitted the code with tabs but CP removed all of the tabs. I then went back in and edited the message and changed the tabs to spaces, and CP _STILL_ removed the whitespace.
Darn. Well, imagine the previous, but with proper formating.
Jeff
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Next time use the < pre > or < code > html tag
Also in the for loop there is a bug, the correct line looks like:
foreach (Process otherProc in Process.GetProcessesByName(proc.ProcessName))
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Balazs, thanks for the tip about the formatting - I just changed my previous posting to use the correct formatting.
Re. GetProcessesByName(proc.ProcessName) vs. GetProcessesByName(assemblyName), my original code is correct and assemblyName should be used, not proc.ProcessName.
When running in the debugger, assemblyName is "MyApp" and proc.ProcessName is "MyApp.vshost" (this is something that VS does), so if you use proc.ProcessName, the procID won't be found and although the new instance of your application won't run, the previous instance's window won't be brought to the front via SetForegroundWindow().
When running normally (and not in the debugger), both proc.ProcessName and assemblyName will be "MyApp".
Try it, you'll see.
foreach (Process otherProc in Process.GetProcessesByName(assemblyName))
...is the way to go.
Thanks,
Jeff
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks! I tried this code with your modification and it works great!
"If you're not sure what option to choose, talk to your system administrator. If you are the systems administrator and you're not sure, select Manual, and consider a career in the food-service industry..." -Excerpt from RedHat Installation Manual
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
You could modify your routine above to the following which will eliminate the need to pass the proc & assembly parameters:
public void RaiseOtherProcess() { Process process = Process.GetCurrentProcess(); Assembly assembly = Assembly.GetCallingAssembly(); ....
The call should always be within the same process regardless of which assembly it originated from. The Assembly.GetCallingAssembly will fail if you are chaining the call through another assembly, but I don't know why you'd be doing that so...
JB

|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi to everyone. Can someone help me to implement that peace of code to win. form. This is new for me, but this is excellent way to learn more complex code writing. If someone already have simple form with Single Process Instance Object feature implemented I'll be grateful if sent it to me. Thanks. Sorry for my poor English.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I agree with the other respondents - this is a great article! I just have one question/concern. You call Release() from within the finalizer. All documentation that I've ever read instructs us not to reference any managed objects from within the finalizer (http://msdn2.microsoft.com/en-us/library/system.idisposable.aspx, Eric Gunnerson's C#, etc. etc. If I'm not mistaken, the using forces a call to Dispose() which neatly cleans up, calling Release() and suppressing finalization. It is therefore highly unlikely that the finalizer would ever be called. If it were however, would not this result in a runtime error? Just curious?
Scott
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
You're quite correct that this is a violation. This created a hard to find bug in an application I'm developing. Here are the suggested changes (which works for me):
...
private bool m_Disposed = false;
...
~SingleProgramInstance() { Dispose(false); }
...
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
private void Dispose(bool disposing) { if (this.m_Disposed == false) { if (disposing) this.Release();
this.m_Disposed = true; } }
The GC will call finalize with Dispose(false) and under this condition no managed objects are referenced. If a user calls Dispose() this will translate to Dispose(true) and managed objects can be referenced.
Nice article though. Very useful.
modified on Tuesday, December 04, 2007 9:19:35 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I've used your suggestions very succesfully, thanks. The only problem is that under W98 I can't show and restore the original application if it is in the tray. Does ShowWindowAsync not work under W98, do you know?
Dave
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks for the excellent job. I had a program finished and the client requested that is it only be openned once. I freaked until I found your article which allowed me to add this feature in less than an hour. This is the kind of article that makes CodeProject so valuable.
Thanks, Joe
More stuff at www.smileymicros.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
This is a great article but the method of "raising" the existing process is is a little complicated and doesn't always work (hidden windows, task tray icons and so on). The workarounds suggested here and elsewhere for these limitations are even more complicated and still don't always work.
I have implemented this basic approach in my applications using a registered message to "wake up" the existing instance. The main disadvantage is that you have to add a little code to your main form; the main advantage is that it works (for me anyway).
Here's the result (you may want to take more care in the choice of the string used to register the message - replace "MyApplication_Wakeup":
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using System.Reflection; namespace SingleInstance { public class SingleProgramInstance : IDisposable { static public uint WakeupMessage = NativeMethods.RegisterWindowMessage("MyApplication_Wakeup"); static IntPtr HWND_BROADCAST = (IntPtr)0xffff; //private members private Mutex _processSync; private bool _owned = false; public SingleProgramInstance() : this("") { } public SingleProgramInstance(string identifier) { // Initialize a named mutex and attempt to get ownership immediately. _processSync = new Mutex(true, Assembly.GetExecutingAssembly().GetName().Name + identifier, out _owned); } ~SingleProgramInstance() { //Release mutex (if necessary) //This should have been accomplished using Dispose() Release(); } public bool IsSingleInstance { //If we don't own the mutex than // we are not the first instance. get { return _owned; } } public void RaiseOtherProcess() { NativeMethods.PostMessage(HWND_BROADCAST, WakeupMessage, IntPtr.Zero, IntPtr.Zero); } private void Release() { if (_owned) { // If we own the mutex then release it so that other "same" processes can now start. _processSync.ReleaseMutex(); _owned = false; } } #region Implementation of IDisposable public void Dispose() { //release mutex (if necessary) and notify // the garbage collector to ignore the destructor Release(); GC.SuppressFinalize(this); } #endregion } class NativeMethods { [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern uint RegisterWindowMessage(string lpString); [DllImport("user32.dll", SetLastError = true)] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); } }
The code needed in the main form class is something like this:
protected override void WndProc(ref Message m) { if ((uint)m.Msg == SingleInstance.SingleProgramInstance.WakeupMessage) { if (WindowState == FormWindowState.Minimized) { Visible = true; WindowState = FormWindowState.Normal; } this.Activate(); } base.WndProc(ref m); }
Regards,
Phil
The opinions expressed in this communication do not necessarily represent those of the author (especially if you find them impolite, discourteous or inflammatory).
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
I couldn't get it to work with PostMessage, the currently running process wasn't recieving the message. However using SendMessage instead solved the problem, here are the changes:
[DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam );
NativeMethods.SendMessage(HWND_BROADCAST, WakeupMessage, IntPtr.Zero, IntPtr.Zero);
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
PostMessage works for me and is to be preferred. If you use SendMessage and some app in the system fails to process the message then your app is hung. You should do everything you can to avoid making other people's bugs look like yours!
The receiving app needs to have a top-level window to process the message; that means (I think) its ShowInTaskbar property needs to be true. It is, however, permissible for the window to be invisible or disabled.
The opinions expressed in this communication do not necessarily represent those of the author (especially if you find them impolite, discourteous or inflammatory).
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
well ... i seem to be a bit more unfortunate ... as none of them is working for me?
The difference between my application and others where it works could possible be: I dont have my application entry in the taskbar, though it works while the first instance Form is behind other windows. It doesn't work only when the Form is hidden.
Please let me know if someone has faced the similar situation and the resolution.
Thanks ........... Saud
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
I converted all the code to VB and was wondering if anyone knows why it might not work with VB. The Mutex object got created with each new application that was run. I've solved the problem by just searching by process name, but would prefer the Mutex method... everything else works fine. The window raises properly and everything.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
thanks for your article, my application is on the tray, i would like to pop it up when user tries to open another instance , do you have an idea what to use.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Well first you'll want to see this bit of code, which is where you would stick your code:
if (IsIconic(hWnd)) { ShowWindowAsync(hWnd,SW_RESTORE); } SetForegroundWindow(hWnd);
Now right before that we add:
if (!IsWindowVisible(hWnd)) { ShowWindowAsync(hWnd,SW_SHOW); }
That should work. You might also need to add some other stuff in there if you need to change the tray icon or some variables. My own tray icon stuff would only need to use that, though.
You'll need to declare the Win32 IsWindowVisible function as well, search msdn.microsoft.com to find the prototype.
If this doesn't work and hWnd is 0, try checkinng that thread below about the hidden main window.
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
For me, while the Window/Form is hidden, the hWnd is always "0" ... hence using SendMessage/PostMessage or any other method that needs hWnd will not work.
Any clues?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
[STAThread] static void Main() { using (SingleProgramInstance spi = new SingleProgramInstance("Unique")) { if (spi.IsSingleInstance) { Application.EnableVisualStyles(); Application.Run(new MainForm()); } else { spi.RaiseOtherProcess(); } } } .NET 2 Beta 2 on Win XP SP2 When you try to run the application many times, no new instances are created (good), however, it doesn't send the focus to the already running instance and when you close the running instance you get "This program has performed an illegal ..." window as many times as the times you tried to run other instances!
Any way, nice work and by the way, are you a relative to Harry Potter ?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hello @all,
Unfortunately i also ran into a similar(?) problem (.NET 2.0 b2). Yes, indeed i get no focus. And when exiting and turning on the debugger, i get the Exception in the following line:
_processSync = new Mutex( true, // desire intial ownership Assembly.GetExecutingAssembly().GetName().Name + identifier, out _owned);
The Exception is:
AbandonedMutexException was unhandled The wait completed due to an abandoned mutex.
Does anyone have some hints? Thanks in advance!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|