Advertisement
fortsoft

SingleInstance

Dec 14th, 2022 (edited)
588
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 24.23 KB | Source Code | 0 0
  1. /**
  2.  * This is open-source software licensed under the terms of the MIT License.
  3.  *
  4.  * Copyright (c) 2009-2023 Petr Červinka - FortSoft <[email protected]>
  5.  *
  6.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  7.  * of this software and associated documentation files (the "Software"), to deal
  8.  * in the Software without restriction, including without limitation the rights
  9.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10.  * copies of the Software, and to permit persons to whom the Software is
  11.  * furnished to do so, subject to the following conditions:
  12.  *
  13.  * The above copyright notice and this permission notice shall be included in all
  14.  * copies or substantial portions of the Software.
  15.  *
  16.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22.  * SOFTWARE.
  23.  **
  24.  * Version 1.1.1.1
  25.  */
  26.  
  27. using System;
  28. using System.Diagnostics;
  29. using System.Globalization;
  30. using System.IO;
  31. using System.Linq;
  32. using System.Runtime.InteropServices;
  33. using System.Text.RegularExpressions;
  34. using System.Threading;
  35. using System.Windows.Forms;
  36.  
  37. namespace FortSoft.Tools {
  38.  
  39.     /// <summary>
  40.     /// Implements a set of methods to ensure that only one instance of the
  41.     /// application runs, or only one instance of a particular form runs, or to
  42.     /// find the main window of the desired application and give it focus.
  43.     /// </summary>
  44.     public static class SingleInstance {
  45.  
  46.         /// <summary>
  47.         /// Windows API constant.
  48.         /// </summary>
  49.         private const int SW_RESTORE = 9;
  50.  
  51.         /// <summary>
  52.         /// Constants.
  53.         /// </summary>
  54.         private const string Local = "Local";
  55.         private const string Underscore = "_";
  56.  
  57.         /// <summary>
  58.         /// Field.
  59.         /// </summary>
  60.         private static Mutex mutex;
  61.  
  62.         /// <summary>
  63.         /// Imports.
  64.         /// </summary>
  65.         [DllImport("user32.dll")]
  66.         private static extern int ShowWindowAsync(IntPtr hWnd, int nCmdShow);
  67.  
  68.         [DllImport("user32.dll")]
  69.         private static extern int SetForegroundWindow(IntPtr hWnd);
  70.  
  71.         [DllImport("user32.dll")]
  72.         private static extern int IsIconic(IntPtr hWnd);
  73.  
  74.         /// <summary>
  75.         /// Restore application window if minimized. Do not restore if already in
  76.         /// normal or maximized window state, since we don't want to change the
  77.         /// current state of the window. Then focus the window.
  78.         /// </summary>
  79.         /// <returns>True if succeeded.</returns>
  80.         public static bool FocusRunning() => FocusRunning(null);
  81.  
  82.         /// <summary>
  83.         /// Restore application window if minimized. Do not restore if already in
  84.         /// normal or maximized window state, since we don't want to change the
  85.         /// current state of the window. Then focus the window.
  86.         /// </summary>
  87.         /// <param name="filePath">Application executable path.</param>
  88.         /// <returns>True if succeeded.</returns>
  89.         public static bool FocusRunning(string filePath) => FocusRunning(filePath, regex: null);
  90.  
  91.         /// <summary>
  92.         /// Restore application window if minimized. Do not restore if already in
  93.         /// normal or maximized window state, since we don't want to change the
  94.         /// current state of the window. Then focus the window.
  95.         /// </summary>
  96.         /// <param name="filePath">Application executable path.</param>
  97.         /// <param name="regex">Regex object to perform comparison.</param>
  98.         /// <returns>True if succeeded.</returns>
  99.         public static bool FocusRunning(string filePath, Regex regex) {
  100.             IntPtr hWnd = GetWindowHandle(filePath, regex);
  101.             if (!hWnd.Equals(IntPtr.Zero)) {
  102.                 if (!IsIconic(hWnd).Equals(0)) {
  103.                     ShowWindowAsync(hWnd, SW_RESTORE);
  104.                 }
  105.                 SetForegroundWindow(hWnd);
  106.                 return true;
  107.             }
  108.             return false;
  109.         }
  110.  
  111.         /// <summary>
  112.         /// Restore application window if minimized. Do not restore if already in
  113.         /// normal or maximized window state, since we don't want to change the
  114.         /// current state of the window. Then focus the window.
  115.         /// </summary>
  116.         /// <param name="filePath">Application executable path.</param>
  117.         /// <param name="mainWindowTitle">Main window title.</param>
  118.         /// <returns>True if succeeded.</returns>
  119.         public static bool FocusRunning(string filePath, string mainWindowTitle) {
  120.             return FocusRunning(filePath, mainWindowTitle, CultureInfo.CurrentCulture);
  121.         }
  122.  
  123.         /// <summary>
  124.         /// Restore application window if minimized. Do not restore if already in
  125.         /// normal or maximized window state, since we don't want to change the
  126.         /// current state of the window. Then focus the window.
  127.         /// </summary>
  128.         /// <param name="filePath">Application executable path.</param>
  129.         /// <param name="mainWindowTitle">Main window title.</param>
  130.         /// <param name="comparisonType">Substring comparison type.</param>
  131.         /// <returns>True if succeeded.</returns>
  132.         public static bool FocusRunning(string filePath, string mainWindowTitle, StringComparison comparisonType) {
  133.             IntPtr hWnd = GetWindowHandle(filePath, mainWindowTitle, comparisonType);
  134.             if (!hWnd.Equals(IntPtr.Zero)) {
  135.                 if (!IsIconic(hWnd).Equals(0)) {
  136.                     ShowWindowAsync(hWnd, SW_RESTORE);
  137.                 }
  138.                 SetForegroundWindow(hWnd);
  139.                 return true;
  140.             }
  141.             return false;
  142.         }
  143.  
  144.         /// <summary>
  145.         /// Restore application window if minimized. Do not restore if already in
  146.         /// normal or maximized window state, since we don't want to change the
  147.         /// current state of the window. Then focus the window.
  148.         /// </summary>
  149.         /// <param name="filePath">Application executable path.</param>
  150.         /// <param name="mainWindowTitle">Main window title.</param>
  151.         /// <param name="culture">Culture for string comparison.</param>
  152.         /// <returns>True if succeeded.</returns>
  153.         public static bool FocusRunning(string filePath, string mainWindowTitle, CultureInfo culture) {
  154.             return FocusRunning(filePath, mainWindowTitle, culture, CompareOptions.None);
  155.         }
  156.  
  157.         /// <summary>
  158.         /// Restore application window if minimized. Do not restore if already in
  159.         /// normal or maximized window state, since we don't want to change the
  160.         /// current state of the window. Then focus the window.
  161.         /// </summary>
  162.         /// <param name="filePath">Application executable path.</param>
  163.         /// <param name="mainWindowTitle">Main window title.</param>
  164.         /// <param name="culture">Culture for string comparison.</param>
  165.         /// <param name="options">Compare options.</param>
  166.         /// <returns>True if succeeded.</returns>
  167.         public static bool FocusRunning(string filePath, string mainWindowTitle, CultureInfo culture, CompareOptions options) {
  168.             IntPtr hWnd = GetWindowHandle(filePath, mainWindowTitle, culture, options);
  169.             if (!hWnd.Equals(IntPtr.Zero)) {
  170.                 if (!IsIconic(hWnd).Equals(0)) {
  171.                     ShowWindowAsync(hWnd, SW_RESTORE);
  172.                 }
  173.                 SetForegroundWindow(hWnd);
  174.                 return true;
  175.             }
  176.             return false;
  177.         }
  178.  
  179.         /// <summary>
  180.         /// Get the first instance that is not this instance, has the same
  181.         /// process name and was started from the same file name and location.
  182.         /// Also check that the process has a valid window handle in this session
  183.         /// to filter out other user's processes.
  184.         /// </summary>
  185.         /// <returns>Window handle.</returns>
  186.         public static IntPtr GetWindowHandle() => GetWindowHandle(null);
  187.  
  188.         /// <summary>
  189.         /// Gets the window handle of the application according to the specified
  190.         /// path to the executable file if another instance of the application is
  191.         /// already running. If null is given, it will get the window handle of
  192.         /// the instance of the current application, if any.
  193.         /// </summary>
  194.         /// <param name="filePath">Application executable path.</param>
  195.         /// <returns>Window handle.</returns>
  196.         public static IntPtr GetWindowHandle(string filePath) => GetWindowHandle(filePath, regex: null);
  197.  
  198.         /// <summary>
  199.         /// Gets the window handle of the application according to the specified
  200.         /// path to the executable file if another instance of the application is
  201.         /// already running. If null is given, it will get the window handle of
  202.         /// the instance of the current application, if any.
  203.         /// </summary>
  204.         /// <param name="filePath">Application executable path.</param>
  205.         /// <param name="regex">Regex object to perform comparison.</param>
  206.         /// <returns>Window handle.</returns>
  207.         public static IntPtr GetWindowHandle(string filePath, Regex regex) {
  208.             IntPtr hWnd = IntPtr.Zero;
  209.             try {
  210.                 Process process = Process.GetCurrentProcess();
  211.                 if (filePath == null) {
  212.                     FileSystemInfo processFileInfo = new FileInfo(process.MainModule.FileName);
  213.                     foreach (Process p in Process.GetProcessesByName(process.ProcessName)
  214.                             .Where(new Func<Process, bool>(p => p.SessionId.Equals(process.SessionId)))
  215.                             .ToArray()) {
  216.  
  217.                         bool eq = !p.Id.Equals(process.Id)
  218.                             && !p.MainWindowHandle.Equals(IntPtr.Zero)
  219.                             && processFileInfo.Name.Equals(new FileInfo(p.MainModule.FileName).Name);
  220.                         if (eq && regex == null || eq && regex.IsMatch(p.MainWindowTitle)) {
  221.                             hWnd = p.MainWindowHandle;
  222.                             break;
  223.                         }
  224.                     }
  225.                 } else {
  226.                     foreach (Process p in Process.GetProcessesByName(Path.GetFileNameWithoutExtension(filePath))
  227.                             .Where(new Func<Process, bool>(p => p.SessionId.Equals(process.SessionId)))
  228.                             .ToArray()) {
  229.  
  230.                         bool eq = string.Compare(p.MainModule.FileName, filePath, StringComparison.OrdinalIgnoreCase).Equals(0)
  231.                             && !p.MainWindowHandle.Equals(IntPtr.Zero);
  232.                         if (eq && regex == null || eq && regex.IsMatch(p.MainWindowTitle)) {
  233.                             hWnd = p.MainWindowHandle;
  234.                             break;
  235.                         }
  236.                     }
  237.                 }
  238.             } catch (Exception exception) {
  239.                 Debug.WriteLine(exception);
  240.             }
  241.             return hWnd;
  242.         }
  243.  
  244.         /// <summary>
  245.         /// Gets the window handle of the application according to the specified
  246.         /// path to the executable file if another instance of the application is
  247.         /// already running. If null is given, it will get the window handle of
  248.         /// the instance of the current application, if any.
  249.         /// </summary>
  250.         /// <param name="filePath">Application executable path.</param>
  251.         /// <param name="mainWindowTitle">Main window title.</param>
  252.         /// <returns>Window handle.</returns>
  253.         public static IntPtr GetWindowHandle(string filePath, string mainWindowTitle) {
  254.             return GetWindowHandle(filePath, mainWindowTitle, CultureInfo.CurrentCulture);
  255.         }
  256.  
  257.         /// <summary>
  258.         /// Gets the window handle of the application according to the specified
  259.         /// path to the executable file if another instance of the application is
  260.         /// already running. If null is given, it will get the window handle of
  261.         /// the instance of the current application, if any.
  262.         /// </summary>
  263.         /// <param name="filePath">Application executable path.</param>
  264.         /// <param name="mainWindowTitle">Main window title.</param>
  265.         /// <param name="comparisonType">Substring comparison type.</param>
  266.         /// <returns>Window handle.</returns>
  267.         public static IntPtr GetWindowHandle(string filePath, string mainWindowTitle, StringComparison comparisonType) {
  268.             IntPtr hWnd = IntPtr.Zero;
  269.             try {
  270.                 Process process = Process.GetCurrentProcess();
  271.                 if (filePath == null) {
  272.                     FileSystemInfo processFileInfo = new FileInfo(process.MainModule.FileName);
  273.                     foreach (Process p in Process.GetProcessesByName(process.ProcessName)
  274.                             .Where(new Func<Process, bool>(p => p.SessionId.Equals(process.SessionId)))
  275.                             .ToArray()) {
  276.  
  277.                         bool eq = !p.Id.Equals(process.Id)
  278.                             && !p.MainWindowHandle.Equals(IntPtr.Zero)
  279.                             && processFileInfo.Name.Equals(new FileInfo(p.MainModule.FileName).Name);
  280.                         if (eq && string.IsNullOrEmpty(mainWindowTitle)
  281.                                 || eq && p.MainWindowTitle.IndexOf(mainWindowTitle, comparisonType) >= 0) {
  282.  
  283.                             hWnd = p.MainWindowHandle;
  284.                             break;
  285.                         }
  286.                     }
  287.                 } else {
  288.                     foreach (Process p in Process.GetProcessesByName(Path.GetFileNameWithoutExtension(filePath))
  289.                             .Where(new Func<Process, bool>(p => p.SessionId.Equals(process.SessionId)))
  290.                             .ToArray()) {
  291.  
  292.                         bool eq = string.Compare(p.MainModule.FileName, filePath, StringComparison.OrdinalIgnoreCase).Equals(0)
  293.                             && !p.MainWindowHandle.Equals(IntPtr.Zero);
  294.                         if (eq && string.IsNullOrEmpty(mainWindowTitle)
  295.                                 || eq && p.MainWindowTitle.IndexOf(mainWindowTitle, comparisonType) >= 0) {
  296.  
  297.                             hWnd = p.MainWindowHandle;
  298.                             break;
  299.                         }
  300.                     }
  301.                 }
  302.             } catch (Exception exception) {
  303.                 Debug.WriteLine(exception);
  304.             }
  305.             return hWnd;
  306.         }
  307.  
  308.         /// <summary>
  309.         /// Gets the window handle of the application according to the specified
  310.         /// path to the executable file if another instance of the application is
  311.         /// already running. If null is given, it will get the window handle of
  312.         /// the instance of the current application, if any.
  313.         /// </summary>
  314.         /// <param name="filePath">Application executable path.</param>
  315.         /// <param name="mainWindowTitle">Main window title.</param>
  316.         /// <param name="culture">Culture for string comparison.</param>
  317.         /// <returns>Window handle.</returns>
  318.         public static IntPtr GetWindowHandle(string filePath, string mainWindowTitle, CultureInfo culture) {
  319.             return GetWindowHandle(filePath, mainWindowTitle, culture, CompareOptions.None);
  320.         }
  321.  
  322.         /// <summary>
  323.         /// Gets the window handle of the application according to the specified
  324.         /// path to the executable file if another instance of the application is
  325.         /// already running. If null is given, it will get the window handle of
  326.         /// the instance of the current application, if any.
  327.         /// </summary>
  328.         /// <param name="filePath">Application executable path.</param>
  329.         /// <param name="mainWindowTitle">Main window title.</param>
  330.         /// <param name="culture">Culture for string comparison.</param>
  331.         /// <param name="options">Compare options.</param>
  332.         /// <returns>Window handle.</returns>
  333.         public static IntPtr GetWindowHandle(string filePath, string mainWindowTitle, CultureInfo culture, CompareOptions options) {
  334.             IntPtr hWnd = IntPtr.Zero;
  335.             try {
  336.                 Process process = Process.GetCurrentProcess();
  337.                 if (filePath == null) {
  338.                     FileSystemInfo processFileInfo = new FileInfo(process.MainModule.FileName);
  339.                     foreach (Process p in Process.GetProcessesByName(process.ProcessName)
  340.                             .Where(new Func<Process, bool>(p => p.SessionId.Equals(process.SessionId)))
  341.                             .ToArray()) {
  342.  
  343.                         bool eq = !p.Id.Equals(process.Id)
  344.                             && !p.MainWindowHandle.Equals(IntPtr.Zero)
  345.                             && processFileInfo.Name.Equals(new FileInfo(p.MainModule.FileName).Name);
  346.                         if (eq && string.IsNullOrEmpty(mainWindowTitle)
  347.                                 || eq && string.Compare(p.MainWindowTitle, mainWindowTitle, culture, options).Equals(0)) {
  348.  
  349.                             hWnd = p.MainWindowHandle;
  350.                             break;
  351.                         }
  352.                     }
  353.                 } else {
  354.                     foreach (Process p in Process.GetProcessesByName(Path.GetFileNameWithoutExtension(filePath))
  355.                             .Where(new Func<Process, bool>(p => p.SessionId.Equals(process.SessionId)))
  356.                             .ToArray()) {
  357.  
  358.                         bool eq = string.Compare(p.MainModule.FileName, filePath, StringComparison.OrdinalIgnoreCase).Equals(0)
  359.                             && !p.MainWindowHandle.Equals(IntPtr.Zero);
  360.                         if (eq && string.IsNullOrEmpty(mainWindowTitle)
  361.                                 || eq && string.Compare(p.MainWindowTitle, mainWindowTitle, culture, options).Equals(0)) {
  362.  
  363.                             hWnd = p.MainWindowHandle;
  364.                             break;
  365.                         }
  366.                     }
  367.                 }
  368.             } catch (Exception exception) {
  369.                 Debug.WriteLine(exception);
  370.             }
  371.             return hWnd;
  372.         }
  373.  
  374.         /// <summary>
  375.         /// Checks if at least one instance of the current application is already
  376.         /// running or not according to mutex.
  377.         /// </summary>
  378.         /// <returns>
  379.         /// Returns true if the current application is already running or on
  380.         /// error to prevent multiple instances; otherwise, false.
  381.         /// </returns>
  382.         public static bool IsRunning() => IsRunning(null);
  383.  
  384.         /// <summary>
  385.         /// Checks if at least one instance of the current application or given
  386.         /// Windows Form of the current application is already running or not
  387.         /// according to mutex. If no Windows Form given, tries to find out if
  388.         /// the current application is running regardless of open forms.
  389.         /// </summary>
  390.         /// <param name="form">A Windows Form to check.</param>
  391.         /// <returns>
  392.         /// Returns true if the current application is already running or on
  393.         /// error to prevent multiple instances; otherwise, false.
  394.         /// </returns>
  395.         public static bool IsRunning(Form form) {
  396.             bool createdNew = false;
  397.             try {
  398.                 string mutexName = Application.CompanyName + Underscore + Application.ProductName;
  399.                 if (form != null) {
  400.                     mutexName += Underscore + form.Name;
  401.                 }
  402.                 mutex = new Mutex(true, Path.Combine(Local, mutexName), out createdNew);
  403.                 if (createdNew) {
  404.                     mutex.ReleaseMutex();
  405.                 }
  406.             } catch (Exception exception) {
  407.                 Debug.WriteLine(exception);
  408.             }
  409.             return !createdNew;
  410.         }
  411.  
  412.         /// <summary>
  413.         /// Begins running a standard application message loop on the current
  414.         /// thread, and makes the specified form visible. If the current
  415.         /// application is already running, tries to give focus to the form of
  416.         /// the running instance.
  417.         /// </summary>
  418.         /// <param name="form">A Windows Form to run.</param>
  419.         public static void Run(Form form) => Run(form, regex: null);
  420.  
  421.         /// <summary>
  422.         /// Begins running a standard application message loop on the current
  423.         /// thread, and makes the specified form visible. If the current
  424.         /// application is already running, tries to give focus to the form of
  425.         /// the running instance.
  426.         /// </summary>
  427.         /// <param name="form">A Windows Form to run.</param>
  428.         /// <param name="regex">Regex object to perform comparison.</param>
  429.         public static void Run(Form form, Regex regex) {
  430.             if (IsRunning(regex == null ? null : form)) {
  431.                 FocusRunning(null, regex);
  432.             } else {
  433.                 Application.Run(form);
  434.             }
  435.         }
  436.  
  437.         /// <summary>
  438.         /// Begins running a standard application message loop on the current
  439.         /// thread, and makes the specified form visible. If the current
  440.         /// application is already running, tries to give focus to the form of
  441.         /// the running instance.
  442.         /// </summary>
  443.         /// <param name="form">A Windows Form to run.</param>
  444.         /// <param name="mainWindowTitle">Main window title.</param>
  445.         public static void Run(Form form, string mainWindowTitle) => Run(form, mainWindowTitle, CultureInfo.CurrentCulture);
  446.  
  447.         /// <summary>
  448.         /// Begins running a standard application message loop on the current
  449.         /// thread, and makes the specified form visible. If the current
  450.         /// application is already running, tries to give focus to the form of
  451.         /// the running instance.
  452.         /// </summary>
  453.         /// <param name="form">A Windows Form to run.</param>
  454.         /// <param name="mainWindowTitle">Main window title.</param>
  455.         /// <param name="comparisonType">Substring comparison type.</param>
  456.         public static void Run(Form form, string mainWindowTitle, StringComparison comparisonType) {
  457.             if (IsRunning(string.IsNullOrEmpty(mainWindowTitle) ? null : form)) {
  458.                 FocusRunning(null, mainWindowTitle, comparisonType);
  459.             } else {
  460.                 Application.Run(form);
  461.             }
  462.         }
  463.  
  464.         /// <summary>
  465.         /// Begins running a standard application message loop on the current
  466.         /// thread, and makes the specified form visible. If the current
  467.         /// application is already running, tries to give focus to the form of
  468.         /// the running instance.
  469.         /// </summary>
  470.         /// <param name="form">A Windows Form to run.</param>
  471.         /// <param name="mainWindowTitle">Main window title.</param>
  472.         /// <param name="culture">Culture for string comparison.</param>
  473.         public static void Run(Form form, string mainWindowTitle, CultureInfo culture) {
  474.             Run(form, mainWindowTitle, culture, CompareOptions.None);
  475.         }
  476.  
  477.         /// <summary>
  478.         /// Begins running a standard application message loop on the current
  479.         /// thread, and makes the specified form visible. If the current
  480.         /// application is already running, tries to give focus to the form of
  481.         /// the running instance.
  482.         /// </summary>
  483.         /// <param name="form">A Windows Form to run.</param>
  484.         /// <param name="mainWindowTitle">Main window title.</param>
  485.         /// <param name="culture">Culture for string comparison.</param>
  486.         /// <param name="options">String comparison options.</param>
  487.         public static void Run(Form form, string mainWindowTitle, CultureInfo culture, CompareOptions options) {
  488.             if (IsRunning(string.IsNullOrEmpty(mainWindowTitle) ? null : form)) {
  489.                 FocusRunning(null, mainWindowTitle, culture, options);
  490.             } else {
  491.                 Application.Run(form);
  492.             }
  493.         }
  494.     }
  495. }
  496.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement