C# Single-Instance Windows Form

Dot Net Perls
Number 1

You need to block new instances of your Windows Forms program from running alongside old ones. New instances must focus old ones if they exist, and then exit. Processes that are new should start normally. Here we see a solution to this problem that is adapted from other code on the Internet, written in the C# programming language.

Implementation

First here we look at the ProcessChecker.cs class that solves this problem. The solution here involves adding the class and adding a line to your Program.cs file. After that it requires minimal maintenance and thought. This solution is adapted from one posted by Lion Shi of Microsoft's support team.

Class that implements process checking [C#]

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System;
using System.Windows.Forms;

/// <summary>
/// Check running processes for an already-running instance. Implements a simple and
/// always effective algorithm to find currently running processes with a main window
/// matching a given substring and focus it.
/// Combines code written by Lion Shi (MS) and Sam Allen.
/// </summary>
static class ProcessChecker
{
    /// <summary>
    /// Stores a required string that must be present in the window title for it
    /// to be detected.
    /// </summary>
    static string _requiredString;

    /// <summary>
    /// Contains signatures for C++ DLLs using interop.
    /// </summary>
    internal static class NativeMethods
    {
	[DllImport("user32.dll")]
	public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

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

	[DllImport("user32.dll")]
	public static extern bool EnumWindows(EnumWindowsProcDel lpEnumFunc,
	    Int32 lParam);

	[DllImport("user32.dll")]
	public static extern int GetWindowThreadProcessId(IntPtr hWnd,
	    ref Int32 lpdwProcessId);

	[DllImport("user32.dll")]
	public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString,
	    Int32 nMaxCount);

	public const int SW_SHOWNORMAL = 1;
    }

    public delegate bool EnumWindowsProcDel(IntPtr hWnd, Int32 lParam);

    /// <summary>
    /// Perform finding and showing of running window.
    /// </summary>
    /// <returns>Bool, which is important and must be kept to match up
    /// with system call.</returns>
    static private bool EnumWindowsProc(IntPtr hWnd, Int32 lParam)
    {
	int processId = 0;
	NativeMethods.GetWindowThreadProcessId(hWnd, ref processId);

	StringBuilder caption = new StringBuilder(1024);
	NativeMethods.GetWindowText(hWnd, caption, 1024);

	// Use IndexOf to make sure our required string is in the title.
	if (processId == lParam && (caption.ToString().IndexOf(_requiredString,
	    StringComparison.OrdinalIgnoreCase) != -1))
	{
	    // Restore the window.
	    NativeMethods.ShowWindowAsync(hWnd, NativeMethods.SW_SHOWNORMAL);
	    NativeMethods.SetForegroundWindow(hWnd);
	}
	return true; // Keep this.
    }

    /// <summary>
    /// Find out if we need to continue to load the current process. If we
    /// don't focus the old process that is equivalent to this one.
    /// </summary>
    /// <param name="forceTitle">This string must be contained in the window
    /// to restore. Use a string that contains the most
    /// unique sequence possible. If the program has windows with the string
    /// "Journal", pass that word.</param>
    /// <returns>False if no previous process was activated. True if we did
    /// focus a previous process and should simply exit the current one.</returns>
    static public bool IsOnlyProcess(string forceTitle)
    {
	_requiredString = forceTitle;
	foreach (Process proc in Process.GetProcessesByName(Application.ProductName))
	{
	    if (proc.Id != Process.GetCurrentProcess().Id)
	    {
		NativeMethods.EnumWindows(new EnumWindowsProcDel(EnumWindowsProc),
		    proc.Id);
		return false;
	    }
	}
	return true;
    }
}

Description. It is a static class. I implement the ProcessChecker logic in a static class. Static classes do not save state, and this class has no state to save. It uses interop functions. We must enumerate the processes to get the window's title text. This is more complex than it should be, but it works. It uses StringBuilder in interop.

Fewer failures. The delegate can restore any window, even one that has been suspended by Windows. Most methods will fail in this case. Send it a substring of the window running, which it uses by calling IndexOf on the processes. This is a simply way to see if the text contains the specified string.

IndexOf String Examples

Use

Here we use the class by adding some code to Program.cs. The code example below will check that no other windows using the string "Program Window Text" are open. If one is, it will be focused and the IsOnlyProcess will return false, preventing the new instance.

Method that uses ProcessChecker [C#]

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
    if (ProcessChecker.IsOnlyProcess("Program Window Text"))
    {
	Application.EnableVisualStyles();
	Application.SetCompatibleTextRenderingDefault(false);
	Application.Run(new TextWindow());
    }
}

Alternatives

Here we look at some alternative solutions to the single-instance requirement in the C# language, and then note some drawbacks they may have. This is not really graceful code, but every other kind of code I used failed at some point, usually after the computer went into sleep mode or was idle.

Steps

Specific problem 1. There's a problem with Mutex. You can use a Mutex to signal to Windows that a certain resource is already active. However, this doesn't bring the running instance to the user's attention.

Specific problem 2. There's a problem with GetProcessesByName. This method finds processes of the specified name, but minimized windows cannot be accessed this way reliably. Windows will release some of the handle resources. The Process collection will lose track of the window name.

Specific problem 3. There's a problem with dialogs. You don't want to show your user a dialog box that says an instance of the program is already running. That is annoying. Almost every professional software program focuses the application.

Summary

The C# programming language

We saw one way you can enforce single-instance windows in Windows Forms. Use this method to handle single-instance C# Windows Forms, and also to automate focusing of existing processes. The biggest drawback is that you must know a substring of the other program's window title Text property.

Windows Forms