Tuesday, February 3, 2009

Alone in the dark - mutex use for single instance of application

Drawback is a denial of service approach

static class Program
{
[STAThread]
static void Main()
{
bool onlyInstance = false;
string mutexName = AssemblyGuid;
// if you want your app to be limited to a single instance across ALL SESSIONS (multiple users & terminal services), then use the following line instead:
// string mutexName = String.Format("Global\\{0}", AssemblyGuid);
Mutex mutex = new Mutex(true, mutexName, out onlyInstance);
if (!onlyInstance) {
IntPtr hwndFirstInstance = WinApi.FindWindow(null, applicationName); // For FindWindow to work, applicationName must be the same as the title (the Text property) of your MainForm.
WinApi.PostMessage(
hwndFirstInstance,
WinApi.WM_SHOWFIRSTINSTANCE,
IntPtr.Zero,
IntPtr.Zero);
return;
}
Application.Run(new MainForm);
GC.KeepAlive(mutex);
}
const string applicationName = "YourApplicationNameGoesHere";
static public string ApplicationName
{
get { return applicationName; } // You could use an Assembly Attribute Accessor instead of a variable, but it involves a bit more code.
}
static public string AssemblyGuid
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(System.Runtime.InteropServices.GuidAttribute), false);
if (attributes.Length == 0) {
return String.Empty;
}
return ((System.Runtime.InteropServices.GuidAttribute)attributes[0]).Value;
}
}
}

public class WinApi
{
public static readonly int WM_SHOWFIRSTINSTANCE = RegisterWindowMessage("WM_SHOWFIRSTINSTANCE");

[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);

[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
public static extern IntPtr FindWindow (string lpClassName, string lpWindowName);

public static void ShowToFront(string windowName)
{
IntPtr window = FindWindow(null, windowName);
ShowWindow(window, 1);
SetForegroundWindow(window);
}
}

public partial class MainForm : System.Windows.Forms.Form
{
bool minimizedToTray;

public MainForm()
{
InitializeComponent();
this.Text = Program.ApplicationName;
}
private void btnMinToTray_Click(object sender, EventArgs e)
{
MinimizeToTray();
}
void MinimizeToTray()
{
notifyIcon = new NotifyIcon();
notifyIcon.Click += new EventHandler(NotifyIconClick);
notifyIcon.Icon = this.Icon;
notifyIcon.Text = Program.ApplicationName;
notifyIcon.Visible = true;
this.ShowInTaskbar = false;
this.Hide();
minimizedToTray = true;
}
public void ShowWindow()
{
if (minimizedToTray) {
notifyIcon.Visible = false;
this.ShowInTaskbar = true;
this.Show();
minimizedToTray = false;
} else {
WinApi.ShowToFront(this.Text);
}
}
void NotifyIconOpen(Object sender, System.EventArgs e)
{
ShowWindow();
}
protected override void WndProc(ref Message message)
{
if (message.Msg == WinApi.WM_SHOWFIRSTINSTANCE) {
ShowWindow();
}
base.WndProc(ref message);
}
}

No comments: