TheDeveloperBlog.com

Home | Contact Us

CSharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript

C# Single-Instance Windows Form

This C# article shows a single instance Windows Form. Further instances are blocked.

Single instance. It is possible to block new instances of a program.

This prevents new ones from running alongside old ones. New instances should focus old ones if they exist, and then exit. Processes that are new should start normally.

Example. 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. It requires minimal maintenance and thought.

Note: This solution is adapted from one posted by Lion Shi of Microsoft's support team.

Based on:

.NET 3.5

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, effective algorithm.
//  Find currently running processes with matching titles.
/// </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;
    }
}

We 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.

Static ClassProcess

Note: This is more complex than it should be, but it works. It uses StringBuilder in interop.

StringBuilder

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.

IndexOfStrings

Use. We use the class by adding code to Program.cs. The code checks 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. We look at some alternative solutions. We then note some drawbacks they may have. This is not 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.

The first problem involves Mutex. You can use a Mutex to signal to Windows that a certain resource is already active. But this doesn't bring the running instance to the user's attention.

Mutex

The second problem is with GetProcessesByName. This method finds processes of the specified name, but minimized windows cannot be reliably accessed this way. Windows will release some of the handle resources.

And: The third problem is with dialogs. You don't want to show your user a dialog box that says an instance of the program is running.

Note: This is annoying. Almost every professional software program focuses the application.

MessageBox.Show

Summary. 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.

Note: The biggest drawback is that you must know a substring of the other program's window title Text property.