What is the correct way to create a single-instance WPF application?
使用C和.NET下# WPF(Windows窗体或控制台比我),什么是正确的方式创建只读的应用可以运行作为一个单实例?
我知道它有一些东西做的事叫做a与mythical互斥,很少能找到人,我可以bothers停止和解释什么是一个命题。
因此,需要通知的代码已经运行的实例,一个用户试图启动一次,也许你的任何命令行参数,如果任何存在。
下面是一篇关于互斥解决方案的非常好的文章。本文所描述的方法有两个优点。
首先,它不需要依赖Microsoft.VisualBasic程序集。如果我的项目已经依赖于这个组件,我可能会提倡使用另一个答案中所示的方法。但事实上,我不使用microsoft.visualBasic程序集,我也不想在我的项目中添加不必要的依赖项。
其次,本文展示了当用户试图启动另一个实例时,如何将应用程序的现有实例置于前台。这是一个非常好的触摸,这里描述的其他互斥解决方案没有解决。
更新截至2014年8月1日,我链接到上面的文章仍然活跃,但是博客已经有一段时间没有更新了。这让我担心,最终它可能会消失,随之而来的是所提倡的解决方案。我在为子孙后代复制这篇文章的内容。这些词只属于博客所有者在健全的自由编码。
Today I wanted to refactor some code that prohibited my application
from running multiple instances of itself.Previously I had use System.Diagnostics.Process to search for an
instance of my myapp.exe in the process list. While this works, it
brings on a lot of overhead, and I wanted something cleaner.Knowing that I could use a mutex for this (but never having done it
before) I set out to cut down my code and simplify my life.In the class of my application main I created a static named Mutex:
1 2 3 4 5 6 | static class Program { static Mutex mutex = new Mutex(true,"{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}"); [STAThread] ... } |
Having a named mutex allows us to stack synchronization across
multiple threads and processes which is just the magic I'm looking
for.Mutex.WaitOne has an overload that specifies an amount of time for us
to wait. Since we're not actually wanting to synchronizing our code
(more just check if it is currently in use) we use the overload with
two parameters: Mutex.WaitOne(Timespan timeout, bool exitContext).
Wait one returns true if it is able to enter, and false if it wasn't.
In this case, we don't want to wait at all; If our mutex is being
used, skip it, and move on, so we pass in TimeSpan.Zero (wait 0
milliseconds), and set the exitContext to true so we can exit the
synchronization context before we try to aquire a lock on it. Using
this, we wrap our Application.Run code inside something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | static class Program { static Mutex mutex = new Mutex(true,"{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}"); [STAThread] static void Main() { if(mutex.WaitOne(TimeSpan.Zero, true)) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); mutex.ReleaseMutex(); } else { MessageBox.Show("only one instance at a time"); } } } |
So, if our app is running, WaitOne will return false, and we'll get a
message box.Instead of showing a message box, I opted to utilize a little Win32 to
notify my running instance that someone forgot that it was already
running (by bringing itself to the top of all the other windows). To
achieve this I used PostMessage to broadcast a custom message to every
window (the custom message was registered with RegisterWindowMessage
by my running application, which means only my application knows what
it is) then my second instance exits. The running application instance
would receive that notification and process it. In order to do that, I
overrode WndProc in my main form and listened for my custom
notification. When I received that notification I set the form's
TopMost property to true to bring it up on top.Here is what I ended up with:
- Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | static class Program { static Mutex mutex = new Mutex(true,"{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}"); [STAThread] static void Main() { if(mutex.WaitOne(TimeSpan.Zero, true)) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); mutex.ReleaseMutex(); } else { // send our Win32 message to make the currently running instance // jump on top of all the other windows NativeMethods.PostMessage( (IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SHOWME, IntPtr.Zero, IntPtr.Zero); } } } |
- NativeMethods.cs
1 2 3 4 5 6 7 8 9 10 | // this class just wraps some Win32 stuff that we're going to use internal class NativeMethods { public const int HWND_BROADCAST = 0xffff; public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME"); [DllImport("user32")] public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); [DllImport("user32")] public static extern int RegisterWindowMessage(string message); } |
- Form1.cs (front side partial)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public partial class Form1 : Form { public Form1() { InitializeComponent(); } protected override void WndProc(ref Message m) { if(m.Msg == NativeMethods.WM_SHOWME) { ShowMe(); } base.WndProc(ref m); } private void ShowMe() { if(WindowState == FormWindowState.Minimized) { WindowState = FormWindowState.Normal; } // get our current"TopMost" value (ours will always be false though) bool top = TopMost; // make our form jump to the top of everything TopMost = true; // set it back to whatever it was TopMost = top; } } |
您可以使用mutex类,但很快就会发现您需要实现代码来传递参数,这样您自己就可以了。嗯,当我阅读ChrisSell的书时,我在WinForms中学习了一个技巧。这个技巧使用了框架中已经存在的逻辑。我不了解你,但是当我了解到我可以在框架中重用的东西时,这通常是我走的路线,而不是重新设计轮子。当然,除非它不能满足我的需要。
当我进入WPF时,我想出了一种方法来使用相同的代码,但是在一个WPF应用程序中。这个解决方案应该根据您的问题满足您的需求。
首先,我们需要创建我们的应用程序类。在这个类中,我们将重写onStartup事件并创建一个名为activate的方法,稍后将使用该方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class SingleInstanceApplication : System.Windows.Application { protected override void OnStartup(System.Windows.StartupEventArgs e) { // Call the OnStartup event on our base class base.OnStartup(e); // Create our MainWindow and show it MainWindow window = new MainWindow(); window.Show(); } public void Activate() { // Reactivate the main window MainWindow.Activate(); } } |
第二,我们需要创建一个类来管理我们的实例。在此之前,我们实际上要重用Microsoft.VisualBasic程序集中的一些代码。因为在本例中,我使用的是C,所以我必须引用程序集。如果您使用的是vb.net,则无需执行任何操作。我们将使用的类是WindowsFormsApplicationBase,它继承了我们的实例管理器,然后利用属性和事件来处理单个实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase { private SingleInstanceApplication _application; private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine; public SingleInstanceManager() { IsSingleInstance = true; } protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs) { // First time _application is launched _commandLine = eventArgs.CommandLine; _application = new SingleInstanceApplication(); _application.Run(); return false; } protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs) { // Subsequent launches base.OnStartupNextInstance(eventArgs); _commandLine = eventArgs.CommandLine; _application.Activate(); } } |
基本上,我们使用vb位来检测单个实例并进行相应的处理。当加载第一个实例时,将激发OnStartup。再次运行应用程序时,将激发OnStartupNextInstance。如您所见,我可以通过事件参数得到命令行上传递的内容。我将值设置为实例字段。您可以在这里解析命令行,或者通过构造函数和对activate方法的调用将命令行传递给您的应用程序。
第三,是时候创建入口点了。我们将利用我们的单实例管理器,而不是像通常那样更新应用程序。
1 2 3 4 5 6 7 8 9 | public class EntryPoint { [STAThread] public static void Main(string[] args) { SingleInstanceManager manager = new SingleInstanceManager(); manager.Run(args); } } |
好吧,我希望您能够遵循每一件事情,并且能够使用这个实现并将其作为您自己的实现。
从这里开始。
跨进程互斥体的一个常见用途是确保一次只能运行程序的实例。完成方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | class OneAtATimePlease { // Use a name unique to the application (eg include your company URL) static Mutex mutex = new Mutex (false,"oreilly.com OneAtATimeDemo"); static void Main() { // Wait 5 seconds if contended – in case another instance // of the program is in the process of shutting down. if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false)) { Console.WriteLine("Another instance of the app is running. Bye!"); return; } try { Console.WriteLine("Running - press Enter to exit"); Console.ReadLine(); } finally { mutex.ReleaseMutex(); } } } |
互斥体的一个好特性是,如果应用程序在没有首先调用release mutex的情况下终止,CLR将自动释放互斥体。
实际上,msdn有一个示例应用程序,C和VB都可以这样做:http://msdn.microsoft.com/en-us/library/ms771662(v=vs.90).aspx
The most common and reliable technique
for developing single-instance
detection is to use the Microsoft .NET
Framework remoting infrastructure
(System.Remoting). The Microsoft .NET
Framework (version 2.0) includes a
type, WindowsFormsApplicationBase,
which encapsulates the required
remoting functionality. To incorporate
this type into a WPF application, a
type needs to derive from it, and be
used as a shim between the application
static entry point method, Main, and
the WPF application's Application
type. The shim detects when an
application is first launched, and
when subsequent launches are
attempted, and yields control the WPF
Application type to determine how to
process the launches.
- 对于C人们只需深呼吸,然后忘记整个"我不想包括VisualBasic DLL"。因为这个以及斯科特·汉塞尔曼所说的,事实上,这几乎是解决这个问题最干净的方法,而且是由比你更了解框架的人设计的。
- 从可用性的角度来看,事实是,如果您的用户正在加载一个应用程序,并且它已经打开了,并且您给了他们一个错误消息,比如
'Another instance of the app is running. Bye' ,那么他们就不会是一个非常快乐的用户。您只需(在GUI应用程序中)切换到该应用程序并传递所提供的参数—或者如果命令行参数没有意义,则必须弹出可能最小化的应用程序。
框架已经对此提供了支持——只是某个白痴将dll命名为
提示:如果您按原样使用这种方法,并且您已经有了一个包含资源等的app.xaml,那么您也需要看看它。
这个代码应该去的主要方法。在这里,你需要更多的信息(在wpf Main方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | [DllImport("user32.dll")] private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow); private const int SW_SHOWMAXIMIZED = 3; static void Main() { Process currentProcess = Process.GetCurrentProcess(); var runningProcess = (from process in Process.GetProcesses() where process.Id != currentProcess.Id && process.ProcessName.Equals( currentProcess.ProcessName, StringComparison.Ordinal) select process).FirstOrDefault(); if (runningProcess != null) { ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED); return; } } |
方法2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | static void Main() { string procName = Process.GetCurrentProcess().ProcessName; // get the list of all processes by that name Process[] processes=Process.GetProcessesByName(procName); if (processes.Length > 1) { MessageBox.Show(procName +" already running"); return; } else { // Application.Run(...); } } |
注:以上方法assumes流程/你的应用有一个唯一的名称。我们在它的工作流程,因为它的两个find if name的任何现有的加工。所以,如果你的应用程序有一个非常普通的名字(即:记事本),以上方法不会工作。
好吧,我有一个一次性的类,可以很容易地用于大多数用例:
这样使用:
1 2 3 4 5 6 7 8 9 10 11 12 | static void Main() { using (SingleInstanceMutex sim = new SingleInstanceMutex()) { if (sim.IsOtherInstanceRunning) { Application.Exit(); } // Initialize program here. } } |
这里是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | /// <summary> /// Represents a <see cref="SingleInstanceMutex"/> class. /// </summary> public partial class SingleInstanceMutex : IDisposable { #region Fields /// <summary> /// Indicator whether another instance of this application is running or not. /// </summary> private bool isNoOtherInstanceRunning; /// <summary> /// The <see cref="Mutex"/> used to ask for other instances of this application. /// </summary> private Mutex singleInstanceMutex = null; /// <summary> /// An indicator whether this object is beeing actively disposed or not. /// </summary> private bool disposed; #endregion #region Constructor /// <summary> /// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class. /// </summary> public SingleInstanceMutex() { this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning); } #endregion #region Properties /// <summary> /// Gets an indicator whether another instance of the application is running or not. /// </summary> public bool IsOtherInstanceRunning { get { return !this.isNoOtherInstanceRunning; } } #endregion #region Methods /// <summary> /// Closes the <see cref="SingleInstanceMutex"/>. /// </summary> public void Close() { this.ThrowIfDisposed(); this.singleInstanceMutex.Close(); } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this.disposed) { /* Release unmanaged ressources */ if (disposing) { /* Release managed ressources */ this.Close(); } this.disposed = true; } } /// <summary> /// Throws an exception if something is tried to be done with an already disposed object. /// </summary> /// <remarks> /// All public methods of the class must first call this. /// </remarks> public void ThrowIfDisposed() { if (this.disposed) { throw new ObjectDisposedException(this.GetType().Name); } } #endregion } |
WPF单实例应用程序是一个新的应用程序,它使用mutex和ipc工具,并将任何命令行参数传递给正在运行的实例。
《C代码# .net单实例应用程序,是参考答案的市场是一个好的开始。
然而,我发现它不做的很好的案例。例如,当存在有一个模态对话框,对话框打开,不管是一个管理的一类的另一种形式如安(箱)或酮(像我unmanaged OpenFileDialog即使使用标准的.net舱)。与原代码,主要活性形式的冰,但无一stays模态,这看起来很奇怪,加上用户必须单击"保持在它的使用的应用程序。
所以,要创建两个类A型行为singleInstance自动为这一切的意识和wpf WinForms应用程序。
Windows窗体:
1)修改程序,这样的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | static class Program { public static readonly SingleInstance Singleton = new SingleInstance(typeof(Program).FullName); [STAThread] static void Main(string[] args) { // NOTE: if this always return false, close & restart Visual Studio // this is probably due to the vshost.exe thing Singleton.RunFirstInstance(() => { SingleInstanceMain(args); }); } public static void SingleInstanceMain(string[] args) { // standard code that was in Main now goes here Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } |
2)修改主窗口类是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public partial class Form1 : Form { public Form1() { InitializeComponent(); } protected override void WndProc(ref Message m) { // if needed, the singleton will restore this window Program.Singleton.OnWndProc(this, m, true); // TODO: handle specific messages here if needed base.WndProc(ref m); } } |
wpf:
1)修改的应用程序页这样的(并确保你看到它的头两页的行动能够代表的主要方法redefine):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public partial class App : Application { public static readonly SingleInstance Singleton = new SingleInstance(typeof(App).FullName); [STAThread] public static void Main(string[] args) { // NOTE: if this always return false, close & restart Visual Studio // this is probably due to the vshost.exe thing Singleton.RunFirstInstance(() => { SingleInstanceMain(args); }); } public static void SingleInstanceMain(string[] args) { // standard code that was in Main now goes here App app = new App(); app.InitializeComponent(); app.Run(); } } |
2)修改主窗口类是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public partial class MainWindow : Window { private HwndSource _source; public MainWindow() { InitializeComponent(); } protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); _source = (HwndSource)PresentationSource.FromVisual(this); _source.AddHook(HwndSourceHook); } protected virtual IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { // if needed, the singleton will restore this window App.Singleton.OnWndProc(hwnd, msg, wParam, lParam, true, true); // TODO: handle other specific message return IntPtr.Zero; } |
鸭子这里是公用事业类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Threading; namespace SingleInstanceUtilities { public sealed class SingleInstance { private const int HWND_BROADCAST = 0xFFFF; [DllImport("user32.dll")] private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int RegisterWindowMessage(string message); [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); public SingleInstance(string uniqueName) { if (uniqueName == null) throw new ArgumentNullException("uniqueName"); Mutex = new Mutex(true, uniqueName); Message = RegisterWindowMessage("WM_" + uniqueName); } public Mutex Mutex { get; private set; } public int Message { get; private set; } public void RunFirstInstance(Action action) { RunFirstInstance(action, IntPtr.Zero, IntPtr.Zero); } // NOTE: if this always return false, close & restart Visual Studio // this is probably due to the vshost.exe thing public void RunFirstInstance(Action action, IntPtr wParam, IntPtr lParam) { if (action == null) throw new ArgumentNullException("action"); if (WaitForMutext(wParam, lParam)) { try { action(); } finally { ReleaseMutex(); } } } public static void ActivateWindow(IntPtr hwnd) { if (hwnd == IntPtr.Zero) return; FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd)); } public void OnWndProc(IntPtr hwnd, int m, IntPtr wParam, IntPtr lParam, bool restorePlacement, bool activate) { if (m == Message) { if (restorePlacement) { WindowPlacement placement = WindowPlacement.GetPlacement(hwnd, false); if (placement.IsValid && placement.IsMinimized) { const int SW_SHOWNORMAL = 1; placement.ShowCmd = SW_SHOWNORMAL; placement.SetPlacement(hwnd); } } if (activate) { SetForegroundWindow(hwnd); FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd)); } } } #if WINFORMS // define this for Winforms apps public void OnWndProc(System.Windows.Forms.Form form, int m, IntPtr wParam, IntPtr lParam, bool activate) { if (form == null) throw new ArgumentNullException("form"); if (m == Message) { if (activate) { if (form.WindowState == System.Windows.Forms.FormWindowState.Minimized) { form.WindowState = System.Windows.Forms.FormWindowState.Normal; } form.Activate(); FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(form.Handle)); } } } public void OnWndProc(System.Windows.Forms.Form form, System.Windows.Forms.Message m, bool activate) { if (form == null) throw new ArgumentNullException("form"); OnWndProc(form, m.Msg, m.WParam, m.LParam, activate); } #endif public void ReleaseMutex() { Mutex.ReleaseMutex(); } public bool WaitForMutext(bool force, IntPtr wParam, IntPtr lParam) { bool b = PrivateWaitForMutext(force); if (!b) { PostMessage((IntPtr)HWND_BROADCAST, Message, wParam, lParam); } return b; } public bool WaitForMutext(IntPtr wParam, IntPtr lParam) { return WaitForMutext(false, wParam, lParam); } private bool PrivateWaitForMutext(bool force) { if (force) return true; try { return Mutex.WaitOne(TimeSpan.Zero, true); } catch (AbandonedMutexException) { return true; } } } // NOTE: don't add any field or public get/set property, as this must exactly map to Windows' WINDOWPLACEMENT structure [StructLayout(LayoutKind.Sequential)] public struct WindowPlacement { public int Length { get; set; } public int Flags { get; set; } public int ShowCmd { get; set; } public int MinPositionX { get; set; } public int MinPositionY { get; set; } public int MaxPositionX { get; set; } public int MaxPositionY { get; set; } public int NormalPositionLeft { get; set; } public int NormalPositionTop { get; set; } public int NormalPositionRight { get; set; } public int NormalPositionBottom { get; set; } [DllImport("user32.dll", SetLastError = true)] private static extern bool SetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl); [DllImport("user32.dll", SetLastError = true)] private static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl); private const int SW_SHOWMINIMIZED = 2; public bool IsMinimized { get { return ShowCmd == SW_SHOWMINIMIZED; } } public bool IsValid { get { return Length == Marshal.SizeOf(typeof(WindowPlacement)); } } public void SetPlacement(IntPtr windowHandle) { SetWindowPlacement(windowHandle, ref this); } public static WindowPlacement GetPlacement(IntPtr windowHandle, bool throwOnError) { WindowPlacement placement = new WindowPlacement(); if (windowHandle == IntPtr.Zero) return placement; placement.Length = Marshal.SizeOf(typeof(WindowPlacement)); if (!GetWindowPlacement(windowHandle, ref placement)) { if (throwOnError) throw new Win32Exception(Marshal.GetLastWin32Error()); return new WindowPlacement(); } return placement; } } public static class FormUtilities { [DllImport("user32.dll")] private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetActiveWindow(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("kernel32.dll")] public static extern int GetCurrentThreadId(); private delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam); [DllImport("user32.dll")] private static extern bool EnumThreadWindows(int dwThreadId, EnumChildrenCallback lpEnumFunc, IntPtr lParam); private class ModalWindowUtil { private const int GW_OWNER = 4; private int _maxOwnershipLevel; private IntPtr _maxOwnershipHandle; private bool EnumChildren(IntPtr hwnd, IntPtr lParam) { int level = 1; if (IsWindowVisible(hwnd) && IsOwned(lParam, hwnd, ref level)) { if (level > _maxOwnershipLevel) { _maxOwnershipHandle = hwnd; _maxOwnershipLevel = level; } } return true; } private static bool IsOwned(IntPtr owner, IntPtr hwnd, ref int level) { IntPtr o = GetWindow(hwnd, GW_OWNER); if (o == IntPtr.Zero) return false; if (o == owner) return true; level++; return IsOwned(owner, o, ref level); } public static void ActivateWindow(IntPtr hwnd) { if (hwnd != IntPtr.Zero) { SetActiveWindow(hwnd); } } public static IntPtr GetModalWindow(IntPtr owner) { ModalWindowUtil util = new ModalWindowUtil(); EnumThreadWindows(GetCurrentThreadId(), util.EnumChildren, owner); return util._maxOwnershipHandle; // may be IntPtr.Zero } } public static void ActivateWindow(IntPtr hwnd) { ModalWindowUtil.ActivateWindow(hwnd); } public static IntPtr GetModalWindow(IntPtr owner) { return ModalWindowUtil.GetModalWindow(owner); } } } |
这里的岩石,例如,允许你有一个单一的两个应用实例。当任何新instances负荷,他们通过他们的题元的主审,冰上行走。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | public partial class App : Application { private static Mutex SingleMutex; public static uint MessageId; private void Application_Startup(object sender, StartupEventArgs e) { IntPtr Result; IntPtr SendOk; Win32.COPYDATASTRUCT CopyData; string[] Args; IntPtr CopyDataMem; bool AllowMultipleInstances = false; Args = Environment.GetCommandLineArgs(); // TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID MessageId = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}"); SingleMutex = new Mutex(false,"AppName"); if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true))) { new Main(); } else if (Args.Length > 1) { foreach (Process Proc in Process.GetProcesses()) { SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero, Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 2000, out Result); if (SendOk == IntPtr.Zero) continue; if ((uint)Result != MessageId) continue; CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT))); CopyData.dwData = IntPtr.Zero; CopyData.cbData = Args[1].Length*2; CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]); Marshal.StructureToPtr(CopyData, CopyDataMem, false); Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem, Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5000, out Result); Marshal.FreeHGlobal(CopyData.lpData); Marshal.FreeHGlobal(CopyDataMem); } Shutdown(0); } } } public partial class Main : Window { private void Window_Loaded(object sender, RoutedEventArgs e) { HwndSource Source; Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); Source.AddHook(new HwndSourceHook(Window_Proc)); } private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled) { Win32.COPYDATASTRUCT CopyData; string Path; if (Msg == Win32.WM_COPYDATA) { CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT)); Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2); if (WindowState == WindowState.Minimized) { // Restore window from tray } // Do whatever we want with information Activate(); Focus(); } if (Msg == App.MessageId) { Handled = true; return new IntPtr(App.MessageId); } return IntPtr.Zero; } } public class Win32 { public const uint WM_COPYDATA = 0x004A; public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; public IntPtr lpData; } [Flags] public enum SendMessageTimeoutFlags : uint { SMTO_NORMAL = 0x0000, SMTO_BLOCK = 0x0001, SMTO_ABORTIFHUNG = 0x0002, SMTO_NOTIMEOUTIFNOTHUNG = 0x0008 } [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)] public static extern uint RegisterWindowMessage(string lpString); [DllImport("user32.dll")] public static extern IntPtr SendMessageTimeout( IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam, SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult); } |
只是一些想法:在某些情况下,要求只有一个应用程序实例不是像您认为的那样"跛脚"。如果一个应用允许一个用户访问一个数据库的多个应用实例(你知道,所有更新用户计算机上应用程序多个实例中打开的所有记录等),那么数据库应用程序等的难度就大得多。首先,对于"名称冲突"的事情,不要使用人可读的名称-使用一个guid,或者更好的是guid+人可读的名称。名称冲突的可能性刚从雷达上消失,互斥设备不在乎。正如有人指出的那样,DoS攻击会很糟糕,但是如果恶意的人遇到了获取互斥体名称并将其合并到他们的应用程序中的麻烦,那么不管怎样,你基本上是一个目标,你必须做更多的工作来保护自己,而不仅仅是篡改互斥体名称。此外,如果使用以下变量:新的互斥体(true,"some guid plus name",out aifirstinstance),您已经有了互斥体是否是第一个实例的指示器。
对于这样一个看似简单的问题,有这么多答案。只是稍微改变一下,这是我解决这个问题的方法。
创建互斥体可能很麻烦,因为jit-er只看到您将它用于代码的一小部分,并希望将其标记为准备好进行垃圾收集。它非常想聪明你认为你不会在这么长时间内使用这个互斥。实际上,只要您的应用程序正在运行,您就希望保持这个互斥体。告诉垃圾收集器不要使用mutex的最好方法是告诉它在不同代的车库收集之外保持它的活动状态。例子:
1 2 3 |
我从这个页面上提出了这个想法:http://www.ai.uga.edu/~mc/singleinstance.html
它看起来像有一个很好的方式,这两种行为:
wpf单实例应用程序
这提供了一个类,你可以添加manages全是互斥的消息和你的两个简化的cruff实施两个在它的简单的小事。
下面的代码演示管我叫冰溶液A单库中的两个应用实例。它的好,因为它也raises事件的另一个实例,当两个attempts开始,和获得的其他命令行审。
它的geared toward wpf辨别,因为它的
这两个代码requires参考
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Program { static void Main() { var applicationId = new Guid("b54f7b0d-87f9-4df9-9686-4d8fd76066dc"); if (SingleInstanceManager.VerifySingleInstance(applicationId)) { SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted; // Start the application } } static void OnOtherInstanceStarted(object sender, StartupEventArgs e) { // Do something in response to another instance starting up. } } |
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | /// <summary> /// A class to use for single-instance applications. /// </summary> public static class SingleInstanceManager { /// <summary> /// Raised when another instance attempts to start up. /// </summary> public static event StartupEventHandler OtherInstanceStarted; /// <summary> /// Checks to see if this instance is the first instance running on this machine. If it is not, this method will /// send the main instance this instance's startup information. /// </summary> /// <param name="guid">The application's unique identifier.</param> /// <returns>True if this instance is the main instance.</returns> public static bool VerifySingleInstace(Guid guid) { if (!AttemptPublishService(guid)) { NotifyMainInstance(guid); return false; } return true; } /// <summary> /// Attempts to publish the service. /// </summary> /// <param name="guid">The application's unique identifier.</param> /// <returns>True if the service was published successfully.</returns> private static bool AttemptPublishService(Guid guid) { try { ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance)); NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None); serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid)); serviceHost.Open(); return true; } catch { return false; } } /// <summary> /// Notifies the main instance that this instance is attempting to start up. /// </summary> /// <param name="guid">The application's unique identifier.</param> private static void NotifyMainInstance(Guid guid) { NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None); EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid)); using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress)) { ISingleInstance singleInstance = factory.CreateChannel(); singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs()); } } /// <summary> /// Creates an address to publish/contact the service at based on a globally unique identifier. /// </summary> /// <param name="guid">The identifier for the application.</param> /// <returns>The address to publish/contact the service.</returns> private static string CreateAddress(Guid guid) { return string.Format(CultureInfo.CurrentCulture,"net.pipe://localhost/{0}", guid); } /// <summary> /// The interface that describes the single instance service. /// </summary> [ServiceContract] private interface ISingleInstance { /// <summary> /// Notifies the main instance that another instance of the application attempted to start. /// </summary> /// <param name="args">The other instance's command-line arguments.</param> [OperationContract] void NotifyMainInstance(string[] args); } /// <summary> /// The implementation of the single instance service interface. /// </summary> private class SingleInstance : ISingleInstance { /// <summary> /// Notifies the main instance that another instance of the application attempted to start. /// </summary> /// <param name="args">The other instance's command-line arguments.</param> public void NotifyMainInstance(string[] args) { if (OtherInstanceStarted != null) { Type type = typeof(StartupEventArgs); ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null); FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic); Debug.Assert(argsField != null); argsField.SetValue(e, args); OtherInstanceStarted(null, e); } } } } |
您不应该使用命名的互斥体来实现单个实例应用程序(或者至少不用于生产代码)。恶意代码可以很容易地做(拒绝服务)你的屁股…
我发现在相似的简单的解决方案,两个山谷Ragan的修饰,但是卷曲的。它是practically一切你需要的和基于标准的微软windowsformsapplicationbase舱。
firstly,你singleinstancecontroller创建这类,你可以使用在所有其他单实例应用程序,它使用Windows的形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using Microsoft.VisualBasic.ApplicationServices; namespace SingleInstanceController_NET { public class SingleInstanceController : WindowsFormsApplicationBase { public delegate Form CreateMainForm(); public delegate void StartNextInstanceDelegate(Form mainWindow); CreateMainForm formCreation; StartNextInstanceDelegate onStartNextInstance; public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance) { // Set whether the application is single instance this.formCreation = formCreation; this.onStartNextInstance = onStartNextInstance; this.IsSingleInstance = true; this.StartupNextInstance += new StartupNextInstanceEventHandler(this_StartupNextInstance); } void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e) { if (onStartNextInstance != null) { onStartNextInstance(this.MainForm); // This code will be executed when the user tries to start the running program again, // for example, by clicking on the exe file. } // This code can determine how to re-activate the existing main window of the running application. } protected override void OnCreateMainForm() { // Instantiate your main application form this.MainForm = formCreation(); } public void Run() { string[] commandLine = new string[0]; base.Run(commandLine); } } } |
然后你可以使用它,在你的程序为:follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using SingleInstanceController_NET; namespace SingleInstance { static class Program { /// <summary> /// The main entry point for the application. /// </summary> static Form CreateForm() { return new Form1(); // Form1 is used for the main window. } static void OnStartNextInstance(Form mainWindow) // When the user tries to restart the application again, // the main window is activated again. { mainWindow.WindowState = FormWindowState.Maximized; } [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); SingleInstanceController controller = new SingleInstanceController(CreateForm, OnStartNextInstance); controller.Run(); } } } |
这两个程序和singleinstancecontroller _应该参考microsoft.visualbasic网络解决方案。如果你只是想reactivate《奔跑的应用作为一个正常的窗口当用户启动的tries运行两个程序,在第二个参数的singleinstancecontroller可能是空的。在给定窗口的实例,maximized冰。
看以下几码。它是一个伟大的和简单的解决方案的两个目标,一个多instances wpf应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private void Application_Startup(object sender, StartupEventArgs e) { Process thisProc = Process.GetCurrentProcess(); if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1) { MessageBox.Show("Application running"); Application.Current.Shutdown(); return; } var wLogin = new LoginWindow(); if (wLogin.ShowDialog() == true) { var wMain = new Main(); wMain.WindowState = WindowState.Maximized; wMain.Show(); } else { Application.Current.Shutdown(); } } |
这是我用的。它结合了进程枚举来执行切换和互斥,以防止"活动的点击器":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public partial class App { [DllImport("user32")] private static extern int OpenIcon(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var p = Process .GetProcessesByName(Process.GetCurrentProcess().ProcessName); foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero)) { OpenIcon(t.MainWindowHandle); SetForegroundWindow(t.MainWindowHandle); Current.Shutdown(); return; } // there is a chance the user tries to click on the icon repeatedly // and the process cannot be discovered yet bool createdNew; var mutex = new Mutex(true,"MyAwesomeApp", out createdNew); // must be a variable, though it is unused - // we just need a bit of time until the process shows up if (!createdNew) { Current.Shutdown(); return; } new Bootstrapper().Run(); } } |
更新2017-01-25。在尝试了几件事情之后,我决定使用visualbasic.dll,它更简单,效果更好(至少对我来说)。我让我以前的答案作为参考…
就像参考文献一样,这是我在不传递参数的情况下所做的(我找不到任何理由这样做…我指的是一个单独的应用程序,其参数可以从一个实例传递到另一个实例)。如果需要文件关联,则应为每个文档安装应用程序(根据用户标准期望)。如果必须将args传递给现有的应用程序,我想我将使用vb dll。
不传递args(只是单实例应用程序),我更喜欢不注册新的窗口消息,也不重写MattDavis解决方案中定义的消息循环。虽然添加一个VisualBasic DLL并不是什么大问题,但是我不希望只添加一个新的引用来执行单实例应用程序。另外,我更喜欢用main来声明一个新类,而不是从app.startup override调用shutdown,以确保尽快退出。
希望有人会喜欢它…或者会有点启发:—)
项目启动类应设置为"singleInstanceApp"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public class SingleInstanceApp { [STAThread] public static void Main(string[] args) { Mutex _mutexSingleInstance = new Mutex(true,"MonitorMeSingleInstance"); if (_mutexSingleInstance.WaitOne(TimeSpan.Zero, true)) { try { var app = new App(); app.InitializeComponent(); app.Run(); } finally { _mutexSingleInstance.ReleaseMutex(); _mutexSingleInstance.Close(); } } else { MessageBox.Show("One instance is already running."); var processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name); { if (processes.Length > 1) { foreach (var process in processes) { if (process.Id != Process.GetCurrentProcess().Id) { WindowHelper.SetForegroundWindow(process.MainWindowHandle); } } } } } } } |
WindowHelper:
1 2 3 4 5 6 7 8 9 10 11 12 13 | using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Threading; namespace HQ.Util.Unmanaged { public class WindowHelper { [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); |
但不使用互斥,简单回答:
1 2 3 4 5 6 | System.Diagnostics; ... string thisprocessname = Process.GetCurrentProcess().ProcessName; if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1) return; |
把它放在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; namespace Sample { static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { //simple add Diagnostics namespace, and these 3 lines below string thisprocessname = Process.GetCurrentProcess().ProcessName; if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1) return; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Sample()); } } } |
您可以将
这里的轻量级解决方案中使用的应用程序,允许两个窗口已经把现有的两个foreground没有resorting或自定义Windows消息的blindly搜索过程的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | [DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr hWnd); static readonly string guid ="<Application Guid>"; static void Main() { Mutex mutex = null; if (!CreateMutex(out mutex)) return; // Application startup code. Environment.SetEnvironmentVariable(guid, null, EnvironmentVariableTarget.User); } static bool CreateMutex(out Mutex mutex) { bool createdNew = false; mutex = new Mutex(false, guid, out createdNew); if (createdNew) { Process process = Process.GetCurrentProcess(); string value = process.Id.ToString(); Environment.SetEnvironmentVariable(guid, value, EnvironmentVariableTarget.User); } else { string value = Environment.GetEnvironmentVariable(guid, EnvironmentVariableTarget.User); Process process = null; int processId = -1; if (int.TryParse(value, out processId)) process = Process.GetProcessById(processId); if (process == null || !SetForegroundWindow(process.MainWindowHandle)) MessageBox.Show("Unable to start application. An instance of this application is already running."); } return createdNew; } |
编辑:你可以也大鸭和creatednew statically初始化互斥锁,但你会需要两个没有dispose /释放互斥体一次,你是好用的。在prefer道贺,保鲜的互斥体的局部信息会自动为disposed(即使在应用closes曾经达到没有尽头的人。
我在这里找不到一个简短的解决方案,所以我希望有人会喜欢:
更新日期:2018-09-20
(顺便说一句,把代码放在你的"program.cs"里)
1 2 3 4 5 6 7 8 9 10 11 | using System.Diagnostics; static void Main() { Process ThisProcess = Process.GetCurrentProcess(); Process[] AllProcesses = Process.GetProcessesByName(ThisProcess.ProcessName); if (AllProcesses.Length > 1) { //Don't put a MessageBox in here because the user could spam this MessageBox. return; } |
//可选代码。如果不希望有人运行,请使用其他名称的".exe":
1 2 3 4 5 6 7 8 9 10 11 12 13 | string exeName = AppDomain.CurrentDomain.FriendlyName; if (exeName !="the name of you're executable.exe") // If you try that in debug mode, don't forget that u don't use ur normal .exe. Debug uses the .vshost.exe. {// You can add here a MessageBox if you want. To point users that the name got changed and maybe what the name should be or something like that^^ MessageBox.Show("The executable name should be "the name of you're executable.exe"", "Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } //Following Code is default code: Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } |
基于命名互斥体的方法不是跨平台的,因为命名互斥体在mono中不是全局的。基于进程枚举的方法没有任何同步,可能导致不正确的行为(例如,同时启动的多个进程可能都会根据时间自动终止)。在控制台应用程序中,基于窗口系统的方法是不可取的。这个基于Divin答案的解决方案解决了所有这些问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | using System; using System.IO; namespace TestCs { public class Program { // The app id must be unique. Generate a new guid for your application. public static string AppId ="01234567-89ab-cdef-0123-456789abcdef"; // The stream is stored globally to ensure that it won't be disposed before the application terminates. public static FileStream UniqueInstanceStream; public static int Main(string[] args) { EnsureUniqueInstance(); // Your code here. return 0; } private static void EnsureUniqueInstance() { // Note: If you want the check to be per-user, use Environment.SpecialFolder.ApplicationData instead. string lockDir = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "UniqueInstanceApps"); string lockPath = Path.Combine(lockDir, $"{AppId}.unique"); Directory.CreateDirectory(lockDir); try { // Create the file with exclusive write access. If this fails, then another process is executing. UniqueInstanceStream = File.Open(lockPath, FileMode.Create, FileAccess.Write, FileShare.None); // Although only the line above should be sufficient, when debugging with a vshost on Visual Studio // (that acts as a proxy), the IO exception isn't passed to the application before a Write is executed. UniqueInstanceStream.Write(new byte[] { 0 }, 0, 1); UniqueInstanceStream.Flush(); } catch { throw new Exception("Another instance of the application is already running."); } } } } |
这是通过事件实现的相同的事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | public enum ApplicationSingleInstanceMode { CurrentUserSession, AllSessionsOfCurrentUser, Pc } public class ApplicationSingleInstancePerUser: IDisposable { private readonly EventWaitHandle _event; /// <summary> /// Shows if the current instance of ghost is the first /// </summary> public bool FirstInstance { get; private set; } /// <summary> /// Initializes /// </summary> /// <param name="applicationName">The application name</param> /// <param name="mode">The single mode</param> public ApplicationSingleInstancePerUser(string applicationName, ApplicationSingleInstanceMode mode = ApplicationSingleInstanceMode.CurrentUserSession) { string name; if (mode == ApplicationSingleInstanceMode.CurrentUserSession) name = $"Local\\{applicationName}"; else if (mode == ApplicationSingleInstanceMode.AllSessionsOfCurrentUser) name = $"Global\\{applicationName}{Environment.UserDomainName}"; else name = $"Global\\{applicationName}"; try { bool created; _event = new EventWaitHandle(false, EventResetMode.ManualReset, name, out created); FirstInstance = created; } catch { } } public void Dispose() { _event.Dispose(); } } |
您还可以使用codefluent运行时,它是一组免费的工具。它提供了一个单实例类来实现单实例应用程序。
解决方案:使用互斥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System; using System.Windows.Forms; using System.Threading; namespace OneAndOnlyOne { static class Program { static String _mutexID =" // generate guid" /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Boolean _isNotRunning; using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning)) { if (_isNotRunning) { Application.Run(new Form1()); } else { MessageBox.Show("An instance is already running."); return; } } } } } |
为C WinForms节省时间的解决方案…
Program.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | using System; using System.Windows.Forms; // needs reference to Microsoft.VisualBasic using Microsoft.VisualBasic.ApplicationServices; namespace YourNamespace { public class SingleInstanceController : WindowsFormsApplicationBase { public SingleInstanceController() { this.IsSingleInstance = true; } protected override void OnStartupNextInstance(StartupNextInstanceEventArgs e) { e.BringToForeground = true; base.OnStartupNextInstance(e); } protected override void OnCreateMainForm() { this.MainForm = new Form1(); } } static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); string[] args = Environment.GetCommandLineArgs(); SingleInstanceController controller = new SingleInstanceController(); controller.Run(args); } } } |
[我已经为下面的控制台和WPF应用程序提供了示例代码。]
您只需要检查
布尔型
if the Mutex instance named"YourApplicationNameHere" was already
created on the system somewhere
布尔型
if this is the first Mutex named"YourApplicationNameHere" on the
system.
控制台应用程序-示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | static Mutex m = null; static void Main(string[] args) { const string mutexName ="YourApplicationNameHere"; bool createdNew = false; try { // Initializes a new instance of the Mutex class with a Boolean value that indicates // whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex, // and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex. using (m = new Mutex(true, mutexName, out createdNew)) { if (!createdNew) { Console.WriteLine("instance is alreday running... shutting down !!!"); Console.Read(); return; // Exit the application } // Run your windows forms app here Console.WriteLine("Single instance app is running!"); Console.ReadLine(); } } catch (Exception ex) { Console.WriteLine(ex.Message); Console.ReadLine(); } } |
WPF实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public partial class App : Application { static Mutex m = null; protected override void OnStartup(StartupEventArgs e) { const string mutexName ="YourApplicationNameHere"; bool createdNew = false; try { // Initializes a new instance of the Mutex class with a Boolean value that indicates // whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex, // and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex. m = new Mutex(true, mutexName, out createdNew); if (!createdNew) { Current.Shutdown(); // Exit the application } } catch (Exception) { throw; } base.OnStartup(e); } protected override void OnExit(ExitEventArgs e) { if (m != null) { m.Dispose(); } base.OnExit(e); } } |
简单地使用一个
1 | System.IO.File.StreamWriter OpenFlag = null; //globally |
和
1 2 3 4 5 6 7 8 | try { OpenFlag = new StreamWriter(Path.GetTempPath() +"OpenedIfRunning"); } catch (System.IO.IOException) //file in use { Environment.Exit(0); } |
如果从其他路径调用exe,我喜欢允许多个实例的解决方案。我修改了charithj溶液方法1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | static class Program { [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow); [DllImport("User32.dll")] public static extern Int32 SetForegroundWindow(IntPtr hWnd); [STAThread] static void Main() { Process currentProcess = Process.GetCurrentProcess(); foreach (var process in Process.GetProcesses()) { try { if ((process.Id != currentProcess.Id) && (process.ProcessName == currentProcess.ProcessName) && (process.MainModule.FileName == currentProcess.MainModule.FileName)) { ShowWindow(process.MainWindowHandle, 5); // const int SW_SHOW = 5; //Activates the window and displays it in its current size and position. SetForegroundWindow(process.MainWindowHandle); return; } } catch (Exception ex) { //ignore Exception"Access denied" } } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } |
我就是这样处理这个问题的。请注意,调试代码仍在测试中。此代码在app.xaml.cs文件的onstartup中。(WPF)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | // Process already running ? if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1) { // Show your error message MessageBox.Show("xxx is already running. If the original process is hung up you may need to restart your computer, or kill the current xxx process using the task manager.","xxx is already running!", MessageBoxButton.OK, MessageBoxImage.Exclamation); // This process Process currentProcess = Process.GetCurrentProcess(); // Get all processes running on the local computer. Process[] localAll = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName); // ID of this process... int temp = currentProcess.Id; MessageBox.Show("This Process ID: " + temp.ToString()); for (int i = 0; i < localAll.Length; i++) { // Find the other process if (localAll[i].Id != currentProcess.Id) { MessageBox.Show("Original Process ID (Switching to): " + localAll[i].Id.ToString()); // Switch to it... SetForegroundWindow(localAll[i].MainWindowHandle); } } Application.Current.Shutdown(); } |
这可能有一些我还没有发现的问题。如果我遇到任何问题,我会更新我的答案。
以下是一个解决方案:
1 2 3 4 5 6 7 8 9 10 11 | Protected Overrides Sub OnStartup(e As StartupEventArgs) Const appName As String ="TestApp" Dim createdNew As Boolean _mutex = New Mutex(True, appName, createdNew) If Not createdNew Then 'app is already running! Exiting the application MessageBox.Show("Application is already running.") Application.Current.Shutdown() End If MyBase.OnStartup(e) End Sub |
这是normally,代码中使用的单实例的Windows应用程序的形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | [STAThread] public static void Main() { String assemblyName = Assembly.GetExecutingAssembly().GetName().Name; using (Mutex mutex = new Mutex(false, assemblyName)) { if (!mutex.WaitOne(0, false)) { Boolean shownProcess = false; Process currentProcess = Process.GetCurrentProcess(); foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName)) { if (!process.Id.Equals(currentProcess.Id) && process.MainModule.FileName.Equals(currentProcess.MainModule.FileName) && !process.MainWindowHandle.Equals(IntPtr.Zero)) { IntPtr windowHandle = process.MainWindowHandle; if (NativeMethods.IsIconic(windowHandle)) NativeMethods.ShowWindow(windowHandle, ShowWindowCommand.Restore); NativeMethods.SetForegroundWindow(windowHandle); shownProcess = true; } } if (!shownProcess) MessageBox.Show(String.Format(CultureInfo.CurrentCulture,"An instance of {0} is already running!", assemblyName), assemblyName, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0); } else { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form()); } } } |
在本地组件是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | [DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern Boolean IsIconic([In] IntPtr windowHandle); [DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern Boolean SetForegroundWindow([In] IntPtr windowHandle); [DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern Boolean ShowWindow([In] IntPtr windowHandle, [In] ShowWindowCommand command); public enum ShowWindowCommand : int { Hide = 0x0, ShowNormal = 0x1, ShowMinimized = 0x2, ShowMaximized = 0x3, ShowNormalNotActive = 0x4, Minimize = 0x6, ShowMinimizedNotActive = 0x7, ShowCurrentNotActive = 0x8, Restore = 0x9, ShowDefault = 0xA, ForceMinimize = 0xB } |
通常,每当我们执行一个.exe时,每次它创建一个独立的Windows进程时,都会有自己的地址空间、资源等。但我们不希望使用这个标准,因为这会阻止我们创建单个流程。可以使用C中的互斥体创建单实例应用程序,本文将对此进行讨论。
此外,如果我们想使应用程序处于顶层,我们可以使用
1 2 | [DllImport("user32")] static extern IntPtr SetForegroundWindow(IntPtr hWnd); |
我向NativeMethods类添加了一个sendmessage方法。
显然,如果应用程序没有显示在任务栏中,那么postmessage方法就可以工作,但是使用sendmessage方法可以解决这个问题。
1 2 3 4 5 6 7 8 9 10 11 | class NativeMethods { public const int HWND_BROADCAST = 0xffff; public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME"); [DllImport("user32")] public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32")] public static extern int RegisterWindowMessage(string message); } |
这是我的2美分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | static class Program { [STAThread] static void Main() { bool createdNew; using (new Mutex(true,"MyApp", out createdNew)) { if (createdNew) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); var mainClass = new SynGesturesLogic(); Application.ApplicationExit += mainClass.tray_exit; Application.Run(); } else { var current = Process.GetCurrentProcess(); foreach (var process in Process.GetProcessesByName(current.ProcessName).Where(process => process.Id != current.Id)) { NativeMethods.SetForegroundWindow(process.MainWindowHandle); break; } } } } } |