|
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 ?
|
|
|
|
|
May be you are setting your notification icon in default constructor of the Form. In this each time as you run the application, constructor will be invoked.
To avoid it move notification icon code in a function from the Form's constructor and depending on the return value of Run() method, call your notification icon code.
Sonork ID 100:25668
|
|
|
|
|
Yes, it's my constructor...
In fact I think that, for better performance, we could use such a method :
<br />
public static bool Run(string frmText)<br />
{<br />
sTitle = frmText;<br />
if( EnumWindows (new EnumWinCallBack(EnumWindowCallBack), 0) == 0)<br />
{<br />
return false;<br />
}<br />
return true;<br />
}<br />
and in the Main :
<br />
if(SingleInstance.SingleApplication.Run("MyForm"))<br />
Application.Run(new MyForm());<br />
So we avoid a construction each time we launch the same app
|
|
|
|
|
But the better way would be to define IsAlreadyRunning( ) method as public.
My primary purpose was to make it simple to use. In way you suggested above, user have to take care of form title himself.
Sonork ID 100:25668
|
|
|
|
|
I was playing around with this class and made some minor adjustments to it. I also had the same issue that it created a new instance of the form, which put extra notify icons in the system tray. To avoid this problem I made a change to the Run method signature:
public static bool Run(string title, System.Type formType)
{
if (IsAlreadyRunning())
{
_title = title;
EnumWindows(new EnumWindowsCallback(EnumWindowsHandler), 0);
return false;
}
Application.Run((System.Windows.Forms.Form)formType.GetConstructor(System.Type.EmptyTypes).Invoke(null));
return true;
}
You call this method by passing in a string matching the .Text property of your main form. Secondly you pass in the Type of your main form. In the example below I have a main form with the .Text property matching "Application Title", and the form class is named MainForm. A simple call to the Run method:
SingleInstance.Run("Application Title", typeof(MainForm));
If the application does not exists it will instatiate a new form object. If it already exists it will activate the existing window. This also improved the performance a lot...
Frode N. Rosand
|
|
|
|
|
Thanks Rosand,
I will consider your suggestion in next version.
Manish
Sonork ID 100:25668
|
|
|
|
|
Why should the notifyicon be left hanging around anyway? Lets consider this. In a normal application, it constructs your form and runs. What happens when the app terminates? The icon must be cleaned up, else the next time you run the app you will have a second icon and so on. So the fact that there is a singleton application class now should make no difference what-so-ever. The form should be modified to clean up it's resources on finalization IMO. Not the SingleApplication class. One reason for not passing the form type as a parameter and constructing it inside the SingleApplication class is because what if you want to pass parameters to your constructor. This becomes messy and in terms of ownership, the SingleApplication should not care how the form is created or destroyed. Further, what if you want several lines of code in the "construction" of your form. For instance to set certain properties etc. before it is shown by the Application.Run(); It may seem like a good idea to pass the type to the class but if you think about it does it really belong there? My answer is no.
|
|
|
|
|
I had the same problem and all I had to do was to make the notifyIcon.visible = false; Then on the event of Resize of the main form, I made the visible state = true. Poof the issue was corrected.
Work around...? yeah maybe... two lines of code.... priceless...
|
|
|
|
|
I think a mutex would be a better way to handle checking for multiple instances. If I am correct, your solution, while elegant, would not handle applications that have no UI.
-Kevin Buchan
|
|
|
|
|
Also, Process Name in the API is limited to the first 15 characters. That means that WindowsAppliation1 and WindowsApplication2 would clash. A mutex is a better solution.
BTW: Make sure you review CreateMutex in the API docs so that you can name your Mutex correctly for Terminal Services.
|
|
|
|
|
Thanks for your feedback, I will revise it soon.
Sonork ID 100:25668
|
|
|
|
|