|
Hi, great code you posted here, it was really helpful, but I think I found a little bug which is not allowing the process to be effectively killed. The thread you create for waiting for the event to be signaled stays active, you need to signal it for so it knows that the handle has changed and no longer exists.
Peace.
|
|
|
|
|
You're right about the thread. I added a signal in Dispose() and a "disposing" flag that is checked in the handler to see whether to exit the thread.
But to the original author...THANK YOU! This is the only one of the many things I've tried that actually works for restoring the app from the system tray. Nice technique.
|
|
|
|
|
Hey, way to go everyone and thanks dadda_mac too. These are great improvements. I haven't been ignoring you, my $%#@# isp has a spam black hole which can't be turned off. I'm really glad you got some use out of my original project, flawed as it was!
Brad Dre
|
|
|
|
|
Your code was great!! Exactly what I needed to restore my system tray application. The changes below made it a little easier for me to reuse the code, but I'm sure someone here could abstract this even further. You did a good job with this, kudos!
Note: I had to edit this, made some more abstractions - hope this helps someone.
using System;
using System.Threading;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace SingleInstanceNoMutex
{
public class SingleInstance : IDisposable
{
private bool disposed = false;
#region "Imported DLLs, kernel32.dll functions"
[DllImport("kernel32.dll")]
static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll")]
static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
[DllImport("kernel32.dll")]
static extern bool SetEvent(IntPtr hEvent);
[DllImport("kernel32.dll")]
static extern IntPtr OpenEvent(UInt32 dwDesiredAccess, bool bInheritable, string lpName);
[DllImport("kernel32.dll")]
static extern bool CloseHandle(IntPtr hHandle);
[DllImport("kernel32.dll")]
static extern bool ResetEvent(IntPtr hEvent);
#endregion
private string EVENT_NAME = string.Format("Local:{0}:{1}:{2}",
Application.CompanyName,
Application.ProductName,
Application.ProductVersion);
private const uint INFINITE = 0xFFFFFFFF;
private const uint SYNCHRONIZE = 0x00100000;
private const uint EVENT_MODIFY_STATE = 0x0002;
private IntPtr eventHandle = IntPtr.Zero;
private Form form = null;
public SingleInstance(Form mainForm)
{
if (InstanceAlreadyExists())
{
SignalInstance();
}
else
{
form = mainForm;
Application.Run(form);
Environment.Exit(0);
}
}
public SingleInstance(ApplicationContext appContext)
{
if (InstanceAlreadyExists())
{
SignalInstance();
}
else
{
form = appContext.MainForm;
Application.Run(appContext);
Environment.Exit(0);
}
}
~SingleInstance()
{
Dispose(false);
}
private bool InstanceAlreadyExists()
{
eventHandle = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, false, EVENT_NAME);
if (eventHandle == IntPtr.Zero)
{
eventHandle = CreateEvent(IntPtr.Zero, true, false, EVENT_NAME);
if (eventHandle != IntPtr.Zero)
{
Thread thread = new Thread(new ThreadStart(WaitForSignal));
thread.Start();
}
return false;
}
else
{
return true;
}
}
private void SignalInstance()
{
if (eventHandle != IntPtr.Zero)
SetEvent(eventHandle);
}
private void WaitForSignal()
{
while (true)
{
uint result = WaitForSingleObject(eventHandle, INFINITE);
if (result == 0)
{
ResetEvent(eventHandle);
if (form != null) restoreInstance();
}
else
{
break;
}
}
}
private delegate void restoreInstanceDel();
private void restoreInstance()
{
if (form.InvokeRequired)
{
form.Invoke(new restoreInstanceDel(restoreInstance));
}
else
{
form.Show();
form.WindowState = FormWindowState.Normal;
form.Activate();
}
}
#region IDisposable Members
protected virtual void Dispose(bool disposeManagedResources)
{
if (!this.disposed)
{
if (disposeManagedResources)
{
if (form != null)
{
form.Dispose();
form = null;
}
}
if (eventHandle != IntPtr.Zero)
CloseHandle(eventHandle);
eventHandle = IntPtr.Zero;
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
I import this into my project (or create a dll and reference) and use it like this:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace MyWindowsApp
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (new SingleInstanceNoMutex.SingleInstance(new frmSearch())) { }
}
}
}
modified on Tuesday, March 4, 2008 4:21 PM
|
|
|
|
|
Hi all!
I wrote another verion that is able to pass command line arguments using named native Windows pipes.
A version that uses WCF and 'NetNamedPipeBinding' would also be possible.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.ComponentModel;
namespace Common
{
public class SingleInstanceManager : IDisposable
{
#region delegates
public delegate void ShowApplicationCallback( String args );
private ShowApplicationCallback m_ShowAppCallback;
public delegate void TerminateApplicationCallback();
private TerminateApplicationCallback m_TerminateAppCallback;
#endregion
#region contained definitions
private class NativeMethods
{
#region flags and enums
[Flags]
internal enum CreateNamedPipeFlags : uint
{
PIPE_TYPE_BYTE = 0,
PIPE_TYPE_MESSAGE = 0x00000004,
PIPE_READMODE_BYTE = 0,
PIPE_READMODE_MESSAGE = 0x00000002,
PIPE_WAIT = 0,
PIPE_ACCESS_INBOUND = 0x00000001,
PIPE_ACCESS_OUTBOUND = 0x00000002,
PIPE_ACCESS_DUPLEX = 0x00000003,
}
internal enum CreateNamedPipeConstants : uint
{
NUMBER_OF_PIPE_INST = 1,
OUT_BUFFER_SIZE = 256,
IN_BUFFER_SIZE = 256,
}
[Flags]
internal enum CreateFileFlags : uint
{
GENERIC_READ = 0x80000000,
GENERIC_WRITE = 0x40000000,
}
internal enum CreateFileConstants : uint
{
OPEN_EXISTING = 3,
}
#endregion
#region native Windows functions
[DllImport( "Kernel32.dll" )]
public static extern SafeFileHandle CreateNamedPipe( string lpName,
[MarshalAs( UnmanagedType.I4 )]
CreateNamedPipeFlags dwOpenMode,
[MarshalAs( UnmanagedType.I4 )]
CreateNamedPipeFlags dwPipeMode,
[MarshalAs( UnmanagedType.I4 )]
CreateNamedPipeConstants nMaxInstances,
[MarshalAs( UnmanagedType.I4 )]
CreateNamedPipeConstants nOutBufferSize,
[MarshalAs( UnmanagedType.I4 )]
CreateNamedPipeConstants nInBufferSize,
[MarshalAs( UnmanagedType.I4 )]
CreateNamedPipeConstants nDefaultTimeOut,
IntPtr lpSecurityAttributes );
[DllImport( "Kernel32.dll", SetLastError = true )]
[return: MarshalAs( UnmanagedType.I4 )]
public extern static Int32 ConnectNamedPipe( SafeFileHandle namedPipe,
IntPtr overlapped );
[DllImport( "Kernel32.dll", SetLastError = true )]
public extern static SafeFileHandle CreateFile( string name,
[MarshalAs( UnmanagedType.I4 )]
CreateFileFlags desiredAccess,
[MarshalAs( UnmanagedType.I4 )]
CreateFileFlags shareMode,
IntPtr securityAttributes,
[MarshalAs( UnmanagedType.I4 )]
CreateFileConstants creationDisposition,
[MarshalAs( UnmanagedType.I4 )]
UInt32 flagsAndAttributes,
IntPtr templateFile );
[DllImport( "Kernel32.dll" )]
[return: MarshalAs( UnmanagedType.Bool )]
public static extern bool DisconnectNamedPipe( SafeFileHandle hNamedPipe );
#endregion
}
#endregion
#region constructors
public SingleInstanceManager( String applicationName,
ShowApplicationCallback showCallback,
TerminateApplicationCallback terminateCallback )
{
m_ShowAppCallback = showCallback;
m_TerminateAppCallback = terminateCallback;
m_MutexName = applicationName;
m_Arguments = Environment.CommandLine;
}
private SingleInstanceManager()
{ }
#endregion
#region public member functions
public void CheckUniqueness()
{
bool isMutexNew = false;
try
{
m_Mutex = new Mutex( true, m_MutexName, out isMutexNew );
if ( isMutexNew == true )
{
ClaimUniqueness();
}
else
{
NotifyUniqueInstance();
}
}
catch ( Exception e )
{
throw new SingleInstanceManagerException(
"SingleInstanceManager could not CheckUniqueness. " +
"Details :" + e.Message );
}
}
#endregion
#region private member functions
private void ClaimUniqueness()
{
CreatePipe();
m_Thread = new Thread( new ThreadStart( WaitForSignal ) );
m_Thread.Start();
}
private void NotifyUniqueInstance()
{
m_Pipe = NativeMethods.CreateFile( @"\\.\pipe\" + m_MutexName,
NativeMethods.CreateFileFlags.GENERIC_WRITE,
0,
IntPtr.Zero,
NativeMethods.CreateFileConstants.OPEN_EXISTING,
0,
IntPtr.Zero );
if ( m_Pipe.IsInvalid )
throw new Win32Exception( Marshal.GetLastWin32Error() );
using ( TextWriter textWriter =
new StreamWriter( new FileStream( m_Pipe, FileAccess.Write ) ) )
{
textWriter.WriteLine( m_Arguments );
textWriter.Flush();
}
m_TerminateAppCallback();
}
private void CreatePipe()
{
m_Pipe = NativeMethods.CreateNamedPipe( @"\\.\pipe\" + m_MutexName,
NativeMethods.CreateNamedPipeFlags.PIPE_ACCESS_DUPLEX,
NativeMethods.CreateNamedPipeFlags.PIPE_TYPE_BYTE |
NativeMethods.CreateNamedPipeFlags.PIPE_READMODE_BYTE |
NativeMethods.CreateNamedPipeFlags.PIPE_WAIT,
NativeMethods.CreateNamedPipeConstants.NUMBER_OF_PIPE_INST,
NativeMethods.CreateNamedPipeConstants.OUT_BUFFER_SIZE,
NativeMethods.CreateNamedPipeConstants.IN_BUFFER_SIZE,
0,
IntPtr.Zero );
if ( m_Pipe.IsInvalid )
throw new Win32Exception( Marshal.GetLastWin32Error() );
m_TextReader = new StreamReader( new FileStream( m_Pipe, FileAccess.Read ) );
}
private void WaitForSignal()
{
while ( true )
{
try
{
NativeMethods.ConnectNamedPipe( m_Pipe, IntPtr.Zero );
m_Arguments = m_TextReader.ReadLine();
m_ShowAppCallback( m_Arguments );
NativeMethods.DisconnectNamedPipe( m_Pipe );
}
catch ( Exception e )
{
throw new SingleInstanceManagerException(
"SingleInstanceManager encountered a problem. " +
"Details :" + e.Message );
}
}
}
#region IDisposable Members
protected virtual void Dispose( bool disposeManagedResources )
{
if ( !this.disposed )
{
if ( m_Pipe != null )
m_Pipe.Dispose();
if ( m_TextReader != null )
m_TextReader.Dispose();
if ( m_Thread != null )
m_Thread.Abort();
disposed = true;
}
}
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
~SingleInstanceManager()
{
Dispose( false );
}
#endregion
#endregion
#region member variables
private bool disposed = false;
private Mutex m_Mutex;
private SafeFileHandle m_Pipe;
private TextReader m_TextReader;
private String m_MutexName;
private String m_Arguments;
private Thread m_Thread;
#endregion
}
[global::System.Serializable]
public class SingleInstanceManagerException : Exception
{
public SingleInstanceManagerException() { }
public SingleInstanceManagerException( string message ) : base( message ) { }
public SingleInstanceManagerException( string message, Exception inner ) : base( message, inner ) { }
protected SingleInstanceManagerException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context )
: base( info, context ) { }
}
}
Cheers,
Mario M.
Dear CodeProject member: Please don't forget to show me how clever you are by rating also this message as "crap/spam/trash" instead of writing a meaningful response!
|
|
|
|
|
This seems like an excellent solution to me, for Windows Forms apps. Thank you for providing it.
Would you happen to know how to structure this for a WPF (Windows Presentation Foundation) app? I'm a WPF beginner; since XAML-compiled WPF apps auto-generate their own Main() function and call it, I'm a bit fuzzy on where we'd put this code.
(btw, the solution on pg 200 of Adam Nathan's "Windows Presentation Foundation Unleashed", doesn't work. mutexIsNew is always true.)
James Hurst
"The greatness of a nation and its moral progress can be judged by the way its animals are treated."
Mahatma Gandhi
|
|
|
|
|
Funny you should ask, until a few days ago I would have had no idea what you were talking about. But, it looks like Smurfcoder has supplied what you need.
As you say, there's no main(), but it looks like he constructs the Event object in the startup class constructor. Definitely cleaner than what I did in C#.
Brad Dre
|
|
|
|
|
Oh geez - the solution was right there?!!! In anycase, I wouldn't say it's 'cleaner' but it does the job. Seems it took a lot of code markup to make it work. Both of you provided a terrific help!
Thank you.
James Hurst
"The greatness of a nation and its moral progress can be judged by the way its animals are treated."
Mahatma Gandhi
|
|
|
|
|
It seems WPF framework doesn't support single instance why dunno. There's couple if single instance solutions, one uses WCF (seems overkill), and one uses Microsoft.VisualBasic.ApplicationServices . The later is prone to be buggy (doesn't surprise me being VB;P).
Smurfcoders solution works, I've tried in an app used by 100+ users. The only change I'd recommend being
http://www.codeproject.com/cs/threads/SingleInstance.asp?msg=2248357#xx2248357xx[^]
Chuck Norris counted to infinity - twice.
|
|
|
|
|
Hi,
Thanks a million!
I'm new to Windows programming and spent two days looking for a solution until I found yours!
I only made some minor changes in the constructor to make the handling a bit easier:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
namespace WindowHide
{
public class SingleInstanceManager : IDisposable
{
private bool disposed = false;
[DllImport( "kernel32.dll" )]
static extern uint WaitForSingleObject( IntPtr hHandle, uint dwMilliseconds );
[DllImport( "kernel32.dll" )]
static extern IntPtr CreateEvent( IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName );
[DllImport( "kernel32.dll" )]
static extern bool SetEvent( IntPtr hEvent );
[DllImport( "kernel32.dll" )]
static extern IntPtr OpenEvent( UInt32 dwDesiredAccess, bool bInheritable, string lpName );
[DllImport( "kernel32.dll" )]
static extern bool CloseHandle( IntPtr hHandle );
[DllImport( "kernel32.dll" )]
static extern bool ResetEvent( IntPtr hEvent );
private const uint INFINITE = 0xFFFFFFFF;
private const string EVENT_NAME = "UM_USER_SHOW_WINDOW";
private const uint SYNCHRONIZE = 0x00100000;
private const uint EVENT_MODIFY_STATE = 0x0002;
private IntPtr m_EventHandle = IntPtr.Zero;
private Window m_Instance;
public delegate void ShowApplicationCallback();
private ShowApplicationCallback m_callback;
public SingleInstanceManager( Window instance, ShowApplicationCallback callback )
{
m_Instance = instance;
m_callback = callback;
m_EventHandle = OpenEvent( EVENT_MODIFY_STATE | SYNCHRONIZE, false, EVENT_NAME );
if ( m_EventHandle == IntPtr.Zero )
{
m_EventHandle = CreateEvent( IntPtr.Zero, true, false, EVENT_NAME );
if ( m_EventHandle != IntPtr.Zero )
{
Thread thread = new Thread( new ThreadStart( WaitForSignal ) );
thread.Start();
}
}
else
{
SetEvent( m_EventHandle );
Environment.Exit( 0 );
}
}
~SingleInstanceManager()
{
Dispose( false );
}
public void SignalEvent()
{
if ( m_EventHandle != IntPtr.Zero )
SetEvent( m_EventHandle );
}
private void WaitForSignal()
{
while ( true )
{
uint result = WaitForSingleObject( m_EventHandle, INFINITE );
if ( result == 0 )
{
m_Instance.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal, m_callback );
}
else
{
return;
}
}
}
#region IDisposable Members
protected virtual void Dispose( bool disposeManagedResources )
{
if ( !this.disposed )
{
if ( m_EventHandle != IntPtr.Zero )
CloseHandle( m_EventHandle );
m_EventHandle = IntPtr.Zero;
disposed = true;
}
}
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
#endregion
}
}
And the window code (WPF) looks like this:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace WindowHide
{
public partial class Window1 : System.Windows.Window
{
SingleInstanceManager m_Event;
public Window1()
{
this.Visibility = Visibility.Hidden;
Application.Current.Exit += new ExitEventHandler( Current_Exit );
m_Event = new SingleInstanceManager(this, ShowApplication);
InitializeComponent();
}
public void ShowApplication()
{
if ( this.Visibility == Visibility.Hidden )
{
this.Visibility = Visibility.Visible;
}
}
void Current_Exit( object sender, ExitEventArgs e )
{
Environment.Exit( 0 );
}
}
}
|
|
|
|
|
Hey, this is great! And, I'm pleased my article helped you. It looks like you've solved the problem of the Event class being dependent on a specific Form (with Window). Also, you've simplified how the worker thread calls into the gui thread.
But, the code doesn't look familiar -- I wasn't able to compile it in the 1.1 framework. I'm thinking I'm behind the times...
Thanks again!
Brad Dre
|
|
|
|
|
Good tip - I compiled it and it does work fine except that it makes the window hidden initially. Or was that just to show an example of making it show itself?
I wonder now (being the greedy sort that I am) how can we change the ShowApplication method to also bring the window to the foreground (ie, to have focus)?
James Hurst
"The greatness of a nation and its moral progress can be judged by the way its animals are treated."
Mahatma Gandhi
|
|
|
|
|
Hi all!
It's me, Smurfcoder (I changed my name to a more serious one )
I'm not only new to Windows Programming, I'm also new to C#, but I think this might work:
public void ShowApplication()
{
if ( !this.IsActive )
{
this.Activate();
}
}
Cheers,
Mario M.
|
|
|
|
|
You almost got a create WPF single instance app. My only change would be
m_EventHandle = CreateEvent(IntPtr.Zero, true, false, EVENT_NAME);
to
m_EventHandle = CreateEvent(IntPtr.Zero, false, false, EVENT_NAME);
Chuck Norris counted to infinity - twice.
|
|
|
|
|
smurfcoder sounds like such a cool code name :P
Anyways here's some implementations for simple single instance that is not secure:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using System.Threading;
using System.Diagnostics;
namespace YourApp
{
///
/// Interaction logic for App.xaml
///
public partial class App : Application
{
//a unique method using Events? can be found here: http://dev.liferus.ru/2008/12/wpf-application-single-instance-in-one.html
//Single instance developed from this site:
//http://www.ai.uga.edu/~mc/SingleInstance.html
Mutex m;
protected override void OnStartup(StartupEventArgs e)
{
//http://pietschsoft.com/category/WPF.aspx
/* Another way. Slower.
// Get Reference to the current Process
Process thisProc = Process.GetCurrentProcess();
// Check how many total processes have the same name as the current one
if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
Application.Current.Shutdown()
else
base.OnStartup(e);
*/
//the fast way everyone likes :P . This one seems ideal for just about any small project :P
bool singleInstanceRunning;
m = new System.Threading.Mutex(true, "{CF9F1919-8202-4b0b-AB74-6F29490BACBC}", out singleInstanceRunning);
if (!singleInstanceRunning)
Application.Current.Shutdown();
else
base.OnStartup(e);
GC.KeepAlive(m);
}
}
}
modified on Friday, April 3, 2009 12:13 PM
|
|
|
|
|
Hello, you had a good idea and you found a nice solution.
Nevertheless this task can be done in a much simpler way using a Mutex:
public static void Main()<br />
{<br />
bool createdNew;<br />
using(Mutex mtx = new Mutex(true , "MyUniqueMutexName_{B8EE61C8-0F08-4a8e-88F0-E5426309DD96}", out createdNew))<br />
{<br />
if (createdNew)<br />
{<br />
Console.WriteLine("Created new - first instance (or mutex was released)");<br />
}<br />
else<br />
{<br />
Console.WriteLine("Not created - not first instance");<br />
<br />
}<br />
<br />
Console.ReadLine();<br />
}
}
I'm not sure if the "local"-prefix must be prepended or if it is prepended implicitly.
Keep on coding...
|
|
|
|
|
But then you cant call the currently running application to do something (like coming to foreground as in the article)
|
|
|
|
|
Yes, as Steve H. said -- using a Mutex on its own doesn't supply a way for the second instance to signal the first instance, which is, as I said, the hard part. Any global OS object that can be created (even a file) can be used to detect the second instance.
Your code is interesting, I'm new to C# and have not seen anything like that. Does the compiler complain if the object casted to IDisposable does not implement the IDisposable interface? I'm guessing yes. But, why would you explicitly call dispose on a .NET managed object? The other puzzling thing to me is why it is Disposed() in the else, but not actually released until after the end of using?
Thanks for your comment!
Brad Dre
drdre2005
|
|
|
|
|
You want to dispose of your handle to the object as soon as you've decided your process is a duplicate. Until it disposes of it, it would be part owner of the object. If the original process exits, and another process attempted to take its place, it would see the object in use and assume that the work was already being done, when in fact no process was doing it.
|
|
|
|
|