Block new instances of our Windows Forms program from running concurrently with old ones, and focus an existing one. We must examine different approaches and choose the most reliable and featureful one. New instances must focus old ones when started if they exist, and then exit. Otherwise, we can allow a new instance to start normally.
The solution here involves adding a class and adding a line to your Program.cs file, but after that, it requires minimal maintenance and thought. First I want to examine many approaches and present what I found to be the best.
The following code is adapted from a solution posted by Lion Shi of Microsoft's support team. This particular class has a fair amount of modifications made by me, and I make no claim to own them. Add this code to a new class in your Windows Forms project.
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 C++ 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 (not an empty 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;
}
}
You add some code to Program.cs in your C# Windows Forms project. The code example below will check that no other windows using the string "Program Window Text" are running on the system. If one is, it will be focused and the IsOnlyProcess will return false, preventing the new instance from running.
/// <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());
}
}
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. Many alternative methods to this are unreliable and flawed.
You can view and then copy and paste this code at my convenient download site. It is probably easiest for you to copy and paste it into a new C# class. The code is reliable, but a bit too complex than I would like it to be.