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
|