|
|
This doesn't seem to work when you are calling another form from a parent form. Any ideas?
PD
|
|
|
|
|
Yes because it is activating front window using process.
Thats the good idea I can add this feature in next update.
Manish Agarwal
manish.k.agarwal @ gmail DOT com
|
|
|
|
|
"Global\" is only supported on Windows 2000 and above, so if need to check the version of the operating system before using it. Mutexes on previous versions cannot have back slashes, so it will error.
David M. Kean
My Blog: Managed from Down Under
|
|
|
|
|
Thanks David but I am asumming that it is only for 2K onwards.
Sonork ID 100:25668 Home Page
|
|
|
|
|
Even without the slash, the mutex is still global. it may add aesthetics, but nothing else.
--
CleaKO The sad part about this instance is that none of the users ever said anything [about the problem].
Pete O`Hanlon Doesn't that just tell you everything you need to know about users?
|
|
|
|
|
Hi!
Although people quite often need the feature to ensure one application instance at a time, this usually isn't the only request to fulfil.
In my experience the next requests always cover the tasks of
1) bringing the running instance to the front, sometimes with setting input focus to a special control, and
2) (most of the time) passing parameters to the running instance.
The first one sounds simple but can be tricky if you want to ensure the window to popup will not just blink three times in the taskbar instead of actually popping up.
The second one can be solved in various ways. I've usually used Remoting to publish the running instance and then pass arguments from the second call.
Perhaps you could cover these topics as well? Would make the article even more useful.
Best regards,
Mav
|
|
|
|
|
#2 is exactly what I'm trying to figure out...
|
|
|
|
|
Hello Manish,
I HAVE THE SAME PROBLEM.SOME TIME WHEN I AM TRY TO SHOW WINDOW. IT BLINK THE WINDOW AND DISAPPER.
IF U HAVE ANY SOLUTION PLEASE TELL ME.
THANKS
If you can think then I Can.
|
|
|
|
|
If you are talking about for the window focus, sample applicate posted with the article does the same, if something else please explain.
Manish Agarwal
manish.k.agarwal @ gmail DOT com
|
|
|
|
|
Actually my form is Hide. then i am trying to show this form Using Showwindow. Some time it works properly. but some time the form is blinked and not show.
If you can think then I Can.
|
|
|
|
|
I guess some problem with your code. Please review it once again or send me at my email id.
Manish Agarwal
manish.k.agarwal @ gmail DOT com
|
|
|
|
|
Ok Manish i will contect u by email.
Thanks & Regards
If you can think then I Can.
|
|
|
|
|
Well done.
I've seen alot of code to do this and thank goodness at last someone does it the right way, namely with a mutex. It is the only truely correct way to do it.
However, in terms of naming the mutex, you might want to add a property or another method to execute a global single instance. That is, one that works across terminal services and XP sessions as well.
Simply add "Global\" as a prefix to the mutex name and Windows API automatically makes a single instance across ALL user sessions.
Further to get the name of the assembly there is an easier way than going to get the file info. Just use Assembly.GetEntryAssembly().GetName().FullName;
Also, instead of enumerating the windows and checking the titles, which is a bit fuzzy, rather use the Process object to get the process and get the MainWindowHandle from the Process object. This is also more efficient, because it doesn't have to run through every open window comparing the titles, it just goes straight to the correct window handle.
|
|
|
|
|
Thanks for your excellent comments. I will keep in mind whenever I update this article next time.
Sonork ID 100:25668 Home Page
|
|
|
|
|
Hi,
Here is the class I created and use that is based on your code.
It caters for global or local single instance apps, as well as specifying your own optional application name to lock on, which could be a guid or whatever, or allowing it to automatically generate one from a guid and the full entry assembly name if not specified.
There are several Run overloads for different configurations. All three parameters are optional.
It also switches to the existing application in a slightly different way, without losing the maximise window state, which happens if you restore the window every time. It first checks if the app is minimised before restoring.
Also, to ensure that the window is forced to the foreground under almost all circustances, it attaches the thread input of our thread to that of the application we switching to before doing the switch.
Example use:
To create a global single instance application. That is only, one instance across all user sessions. That is, only one instance on the machine.
<br />
[STAThread]<br />
static void Main() <br />
{<br />
SingletonApplication.Run(new Form1(), true);<br />
}<br />
To create a per user single instance application. This works with terminal services, XP sessions and Citrix.
<br />
[STAThread]<br />
static void Main() <br />
{<br />
SingletonApplication.Run(new Form1());<br />
}<br />
The class:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace CompanyName.Win32.Util
{
#region SingletonApplication
public sealed class SingletonApplication
{
#region Public Members
#region Run
public static bool Run(string name, System.Windows.Forms.Form form, bool global)
{
if (InternalIsRunning(name, global))
{
SwitchToCurrentInstance();
return false;
}
Application.Run(form);
return true;
}
public static bool Run(string name, System.Windows.Forms.Form form)
{
return Run(name, form, false);
}
public static bool Run(System.Windows.Forms.Form form, bool global)
{
return Run(GetApplicationName(), form, global);
}
public static bool Run(System.Windows.Forms.Form form)
{
return Run(GetApplicationName(), form, false);
}
public static bool Run(string name, bool global)
{
return !InternalIsRunning(name, global);
}
public static bool Run(string name)
{
return Run(name, false);
}
public static bool Run(bool global)
{
return Run(GetApplicationName(), global);
}
public static bool Run()
{
return Run(GetApplicationName(), false);
}
#endregion
#region IsRunning
public static bool IsRunning(string name, bool global)
{
bool createdNew;
Mutex _mutex = new Mutex(true, GetMutexName(name, global), out createdNew);
if (createdNew)
_mutex.ReleaseMutex();
return !createdNew;
}
public static bool IsRunning(string name)
{
return IsRunning(name, false);
}
public static bool IsRunning(bool global)
{
return IsRunning(GetApplicationName(), global);
}
public static bool IsRunning()
{
return IsRunning(GetApplicationName(), false);
}
#endregion
#region GetApplicationName
public static string GetApplicationName()
{
return string.Format("a37e5577-a9c5-4837-ad17-288b9eb7b682, {0}",
Assembly.GetEntryAssembly().GetName().FullName);
}
#endregion
#endregion
#region Private Members
#region InternalIsRunning
private static bool InternalIsRunning(string name, bool global)
{
if (mutex == null)
{
mutex = new Mutex(true, GetMutexName(name, global));
GC.KeepAlive(mutex);
}
return !mutex.WaitOne(0, false);
}
#endregion
#region GetMutexName
private static string GetMutexName(string name, bool global)
{
return string.Format(@"{0}\{1}",
global ? "Global" : "Local", name.Replace(@"\", "_"));
}
#endregion
#region SwitchToCurrentInstance
private static void SwitchToCurrentInstance()
{
IntPtr hWnd = GetCurrentInstanceWindowHandle();
if (hWnd != IntPtr.Zero)
{
IntPtr _hWnd = GetForegroundWindow();
if (IsIconic(hWnd) != 0)
ShowWindow(hWnd, SW_RESTORE);
int hThread = GetWindowThreadProcessId(hWnd, IntPtr.Zero);
int _hThread = GetWindowThreadProcessId(_hWnd, IntPtr.Zero);
if (hThread != _hThread)
{
AttachThreadInput(_hThread, hThread, 1);
SetForegroundWindow(hWnd);
AttachThreadInput(_hThread, hThread, 0);
}
else
SetForegroundWindow(hWnd);
}
}
#endregion
#region GetCurrentInstanceWindowHandle
private static IntPtr GetCurrentInstanceWindowHandle()
{
IntPtr hWnd = IntPtr.Zero;
Process process = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(process.ProcessName);
foreach(Process _process in processes)
{
if (_process.Id != process.Id &&
_process.MainModule.FileName == process.MainModule.FileName &&
_process.MainWindowHandle != IntPtr.Zero)
{
hWnd = _process.MainWindowHandle;
break;
}
}
return hWnd;
}
#endregion
#region API Imports
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
[DllImport("user32.dll")]
private static extern int IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int AttachThreadInput(int idAttach, int idAttachTo, int fAttach);
#endregion
#region Constructor
private SingletonApplication()
{}
#endregion
private const int SW_RESTORE = 9;
private static Mutex mutex;
#endregion
}
#endregion
}
|
|
|
|
|
Yep, this is the way to do it. Simply checking against window title is not enough. Used to be able to set window class name in Win32 and just find a window by class name.
|
|
|
|
|
This is a vast improvement over the original poster's article. Thanks for posting this correction.
|
|
|
|
|
Here is a updated version.
Changed:
1. Scope is now an enum to support future scopes, not just local and global. Such as per assembly, per user like isolated storage works.
2. Options is now a flags enum, to support future extensibility.
3. I no longer pass the form object into the class. In many cases you dont even want to construct your main form if another instance is running. You may also wish to manipulate the Application object and choose to "run" a different way. Thus your Main should now look as follows:
static void Main()
{
if (SingletonApplication.Run(SingletonApplicationScope.Local,
SingletoneApplicationOptions.SwitchToCurrentInstance)
{
Form1 form1 = new Form1();
Application.Run(form1);
}
}
4. Added SingletonApplication.Exit. This allows you to release th mutex early. This is useful for restarting an application on an unhandled exception. You can call SingletonApplication.Exit before calling Application.Restart, thus allowing you to start another instance of yourself from yourself.
5. I don't use the .NET Framework Mutex object as .NET 1.1 does not support the required security to be able to use this in a Citrix or terminal services environment, including Windows XP multi-user mode. In .NET 2.0 they fixed this, by adding security to the Mutext object. Thus you can replace Win32Mutex by the .NET Mutex object on .NET 2.0, but not on .NET 1.1.
6. Switching to the current instance on .NET 1.1 results in all kinds of wierd exceptions and security issues under Citrix and terminal services. This has been completely redone to cater for the exceptions and avoid them where possible. For instance, iterating the processes, a process may have exited while you iterating over it and when you attempt to query info off it, an exception is thrown. Security exceptions are thrown for other user's processes. The Process object in .NET 1.1 used WMI, thus have to avoid using certain properties and methods to avoid exceptions in a locked down server.
#region SingletonApplicationScope
public enum SingletonApplicationScope
{
None,
Local,
Global
}
#endregion
#region SingletonApplicationOptions
[Flags]
public enum SingletonApplicationOptions
{
None = 0x0,
SwitchToCurrentInstance = 0x1
}
#endregion
#region SingletonApplication
public sealed class SingletonApplication
{
#region Public Members
#region Run
public static bool Run()
{
return InternalRun(SingletonApplicationScope.None, SingletonApplicationOptions.None, GetDefaultApplicationName());
}
public static bool Run(SingletonApplicationScope scope)
{
return InternalRun(scope, SingletonApplicationOptions.None, GetDefaultApplicationName());
}
public static bool Run(SingletonApplicationScope scope, SingletonApplicationOptions options)
{
return InternalRun(scope, options, GetDefaultApplicationName());
}
public static bool Run(SingletonApplicationScope scope, SingletonApplicationOptions options, string applicationName)
{
if (applicationName == null)
throw new ArgumentNullException("applicationName");
if (applicationName.IndexOf('\\') != -1)
{
throw new ArgumentException(
Resources.ResourceManager[Resources.ArgumentExceptionSingletonApplicationName],
"applicationName");
}
return InternalRun(scope, options, applicationName);
}
#endregion
#region IsRunning
public static bool IsRunning()
{
return InternalIsRunning(SingletonApplicationScope.None, GetDefaultApplicationName());
}
public static bool IsRunning(SingletonApplicationScope scope)
{
return InternalIsRunning(scope, GetDefaultApplicationName());
}
public static bool IsRunning(SingletonApplicationScope scope, string applicationName)
{
if (applicationName == null)
throw new ArgumentNullException("applicationName");
if (applicationName.IndexOf('\\') != -1)
{
throw new ArgumentException(
Resources.ResourceManager[Resources.ArgumentExceptionSingletonApplicationName],
"applicationName");
}
return InternalIsRunning(scope, applicationName);
}
#endregion
public static void Exit()
{
if (_mutex != null)
{
_mutex.Close();
_mutex = null;
}
}
public static string GetDefaultApplicationName()
{
return Assembly.GetEntryAssembly().GetName().FullName;
}
#endregion
#region Private Members
private static bool InternalRun(SingletonApplicationScope scope, SingletonApplicationOptions options, string applicationName)
{
string mutexName = GetMutexName(scope, applicationName);
bool createdNew;
Win32Mutex mutex = new Win32Mutex(true, mutexName, out createdNew, true);
if (!createdNew)
{
mutex.Close();
if (IsOptionSet(options, SingletonApplicationOptions.SwitchToCurrentInstance))
SwitchToCurrentInstance();
return false;
}
_mutex = mutex;
return true;
}
private static bool InternalIsRunning(SingletonApplicationScope scope, string applicationName)
{
bool createdNew = false;
string mutexName = GetMutexName(scope, applicationName);
using (Win32Mutex mutex = new Win32Mutex(true, mutexName, out createdNew, true))
{
if (createdNew)
mutex.ReleaseMutex();
}
return !createdNew;
}
private static string GetMutexName(SingletonApplicationScope scope, string applicationName)
{
return String.Concat(GetMutexScope(scope),
typeof(SingletonApplication).GUID.ToString("D", CultureInfo.InvariantCulture),
applicationName);
}
private static string GetMutexScope(SingletonApplicationScope scope)
{
switch (scope)
{
case SingletonApplicationScope.Local: return @"Local\";
case SingletonApplicationScope.Global: return @"Global\";
}
return String.Empty;
}
private static bool IsOptionSet(SingletonApplicationOptions options, SingletonApplicationOptions option)
{
return ((options & option) == option);
}
private static void SwitchToCurrentInstance()
{
IntPtr currentInstanceWindowHandle = GetCurrentInstanceWindowHandle();
if (currentInstanceWindowHandle != IntPtr.Zero)
SetForegroundWindow(currentInstanceWindowHandle);
}
private static void SetForegroundWindow(IntPtr targetWindowHandle)
{
IntPtr foregroundWindowHandle = User32.GetForegroundWindow();
if (targetWindowHandle != foregroundWindowHandle)
{
uint targetWindowThreadId = User32.GetWindowThreadProcessId(targetWindowHandle, IntPtr.Zero);
uint currentThreadId = Kernel32.GetCurrentThreadId();
if (currentThreadId == targetWindowThreadId)
User32.SetForegroundWindow(targetWindowHandle);
else
{
User32.AttachThreadInput(currentThreadId, targetWindowThreadId, true);
User32.SetForegroundWindow(targetWindowHandle);
User32.AttachThreadInput(currentThreadId, targetWindowThreadId, false);
}
}
if (User32.IsIconic(targetWindowHandle))
User32.ShowWindow(targetWindowHandle, User32.ShowWindowCommand.SW_RESTORE);
}
private static IntPtr GetCurrentInstanceWindowHandle()
{
Process currentProcess = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(currentProcess.ProcessName);
foreach (Process process in processes)
{
if (process.Id != currentProcess.Id)
{
IntPtr handle = process.MainWindowHandle;
if (handle != IntPtr.Zero)
{
try
{
if (process.MainModule.FileName == currentProcess.MainModule.FileName)
return handle;
}
catch(InvalidOperationException)
{
if (!process.HasExited)
throw;
}
}
}
}
return IntPtr.Zero;
}
private SingletonApplication()
{
throw new NotSupportedException();
}
private static Win32Mutex _mutex;
#endregion
}
#endregion
Win32Mutex object:
public class Win32Mutex : WaitHandle
{
#region Constructors
public Win32Mutex()
{
bool createdNew;
_handle = CreateMutex(false, null, false, out createdNew);
}
public Win32Mutex(bool initiallyOwned)
{
bool createdNew;
_handle = CreateMutex(initiallyOwned, null, false, out createdNew);
}
public Win32Mutex(bool initiallyOwned, string name)
{
bool createdNew;
_handle = CreateMutex(initiallyOwned, name, false, out createdNew);
}
public Win32Mutex(bool initiallyOwned, string name, out bool createdNew)
{
_handle = CreateMutex(initiallyOwned, name, false, out createdNew);
}
public Win32Mutex(bool initiallyOwned, string name, out bool createdNew, bool allAccess)
{
_handle = CreateMutex(initiallyOwned, name, allAccess, out createdNew);
}
#endregion
#region Public Members
public override IntPtr Handle
{
get { return _handle; }
set { _handle = value; }
}
public void ReleaseMutex()
{
if (_handle == WaitHandle.InvalidHandle)
throw new ObjectDisposedException(GetType().FullName);
ReleaseMutex(_handle);
}
#region WaitOne
public override bool WaitOne()
{
if (_handle == WaitHandle.InvalidHandle)
throw new ObjectDisposedException(GetType().FullName);
return WaitForSingleObject(_handle, Kernel32.INFINITE);
}
public override bool WaitOne(int millisecondsTimeout, bool exitContext)
{
if (_handle == WaitHandle.InvalidHandle)
throw new ObjectDisposedException(GetType().FullName);
return WaitForSingleObject(_handle, millisecondsTimeout);
}
public override bool WaitOne(TimeSpan timeout, bool exitContext)
{
if (_handle == WaitHandle.InvalidHandle)
throw new ObjectDisposedException(GetType().FullName);
return WaitForSingleObject(_handle, (int)timeout.TotalMilliseconds);
}
#endregion
#endregion
#region Protected Members
protected override void Dispose(bool explicitDisposing)
{
if (_handle != WaitHandle.InvalidHandle)
{
CloseHandle(_handle);
_handle = WaitHandle.InvalidHandle;
}
}
#endregion
#region Private Members
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
private IntPtr CreateMutex(bool initiallyOwned, string name, bool allAccess, out bool createdNew)
{
Kernel32.SECURITY_ATTRIBUTES lpMutexAttributes = null;
if (allAccess)
{
lpMutexAttributes = new Kernel32.SECURITY_ATTRIBUTES();
lpMutexAttributes.nLength = Marshal.SizeOf(lpMutexAttributes);
lpMutexAttributes.bInheritHandle = true;
lpMutexAttributes.lpSecurityDescriptor = 0;
int securityDescriptorSize = 0;
if (!AdvApi32.ConvertStringSecurityDescriptorToSecurityDescriptor(
SDDL_MUTEX_ALL_ACCESS_EVERYONE,
AdvApi32.SDDL_REVISION_1,
ref lpMutexAttributes.lpSecurityDescriptor,
ref securityDescriptorSize))
{
throw new Win32Exception();
}
}
try
{
IntPtr handle = Kernel32.CreateMutex(lpMutexAttributes, initiallyOwned, name);
int lastWin32Error = Marshal.GetLastWin32Error();
if (handle == IntPtr.Zero)
{
throw new Win32Exception(lastWin32Error);
}
createdNew = (lastWin32Error != Kernel32.ERROR_ALREADY_EXISTS);
return handle;
}
finally
{
if (lpMutexAttributes != null)
{
if (Kernel32.LocalFree((IntPtr)lpMutexAttributes.lpSecurityDescriptor) != IntPtr.Zero)
{
throw new Win32Exception();
}
}
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
private bool WaitForSingleObject(IntPtr handle, int millisecondsTimeout)
{
int ret;
if ((ret = Kernel32.WaitForSingleObject(handle, millisecondsTimeout)) == Kernel32.WAIT_FAILED)
{
throw new Win32Exception();
}
return (ret != Kernel32.WAIT_TIMEOUT);
}
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
private void ReleaseMutex(IntPtr handle)
{
if (!Kernel32.ReleaseMutex(handle))
{
throw new Win32Exception();
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
private void CloseHandle(IntPtr handle)
{
if (!Kernel32.CloseHandle(handle))
{
throw new Win32Exception();
}
}
private const string SDDL_MUTEX_ALL_ACCESS_EVERYONE = "D:(A;NP;0x001f0001;;;WD)";
private IntPtr _handle = WaitHandle.InvalidHandle;
#endregion
-- modified at 2:59 Sunday 18th December, 2005
|
|
|
|
|
Very elegant approach, I am using NET 2.0 so I will definitely use it ...
I have one question: I pass the path to a file to my application as a parameter . If I re-run the application by passing it another file path, the previously minimized window appear but the new parameter is not taken into account. How do I modify your code to take it into account.
Thanks.
|
|
|
|
|
Now I'm searching for a working single instance application class which works, and I finally found your solution.
Unfortunately I don't get it to run. I fiddle about the User32, Kernel32 and AdvApi32 classes by creating my own ones as they're not provided. Most thinks I could do but I'm facing a lot of problems as there're hundrets of possibilities with Interop. Could you provide your Kernel32, User32 and AdvApi32 classes or a link to them.
Thanks in advance.
Jörg Kaumanns
|
|
|
|
|
Hi.
I wonder if you can help me in a side issue arising from your comment. I am trying to find out all the ways that process.MainModule.FileName can fail (apart from the documented exceptions NotSupportedException and PlatformNotSupportedException). This was the only reference I found to the possibilty of it throwing an InvalidOperationException. Can you tell me how you found out about this - whether it is documented, or if you discovered it by experience. I assume from your code that it occurs if the process has exited, and possibly under other circumstances. Any information would be helpful. Thanks.
Dave
|
|
|
|
|
I have added the mach005 april 2004 code version to my app - I have .Net 1.0.
It works in that it doesn't start a new version of the app if there is already one running.
My requirements are slightly different in that my app is minimized as a system tray icon and does not show up in the taskbar.
When I launch another version it reverts to the running version as expected but the running version stays minimized in the system tray and is not maximized. Also an additional icon appears in the system tray but if you mouse over the original icon it disappears leaving a single sys tray icon.
Any help would be great! Thanks!
-Billy
|
|
|
|
|
Good code man!!!!!
Maharishi Bhatia
|
|
|
|
|
I've just tested your class with a winform that owns a notifyicon, and this icon appears both if I re-launch the app (I have 3 icons if I launch the app three times ...). Do you know what could be the problem ?
|
|
|
|
|