HomeSearch

C# Single Instance Windows Form

Develop a single instance Windows Form to block further instances of the program.

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.

Here: We implement the ProcessChecker logic in a static class. It uses interop functions.

Static

Note: We must enumerate the processes to get the window's title text. This is more complex than it should be, but it works.

Process

Info: The delegate can restore any window, even one that has been suspended by Windows. Most methods will fail in this case.

Tip: Send it a substring of the window running, which it uses by calling IndexOf on the processes.

IndexOf
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; } }

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

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.
Home
Dot Net Perls
© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to info@dotnetperls.com.