C# 创建单实例 WPF 应用程序的正确方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19147/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-01 09:02:17  来源:igfitidea点击:

What is the correct way to create a single-instance WPF application?

提问by Nidonocu

Using C# and WPF under .NET (rather than Windows Formsor console), what is the correct way to create an application that can only be run as a single instance?

在 .NET(而不是Windows 窗体或控制台)下使用 C# 和 WPF ,创建只能作为单个实例运行的应用程序的正确方法是什么?

I know it has something to do with some mythical thing called a mutex, rarely can I find someone that bothers to stop and explain what one of these are.

我知道它与某种称为互斥锁的神秘事物有关,我很少能找到愿意停下来解释其中一个是什么的人。

The code needs to also inform the already-running instance that the user tried to start a second one, and maybe also pass any command-line arguments if any existed.

代码还需要通知已经运行的实例用户试图启动第二个实例,并且可能还传递任何命令行参数(如果存在)。

采纳答案by Matt Davis

Here is a very good articleregarding the Mutex solution. The approach described by the article is advantageous for two reasons.

这是一篇关于 Mutex 解决方案的非常好的文章。文章中描述的方法具有优势,原因有二。

First, it does not require a dependency on the Microsoft.VisualBasic assembly. If my project already had a dependency on that assembly, I would probably advocate using the approach shown in another answer. But as it is, I do not use the Microsoft.VisualBasic assembly, and I'd rather not add an unnecessary dependency to my project.

首先,它不需要依赖于 Microsoft.VisualBasic 程序集。如果我的项目已经依赖于该程序集,我可能会提倡使用另一个答案中显示的方法。但实际上,我不使用 Microsoft.VisualBasic 程序集,而且我不想向我的项目添加不必要的依赖项。

Second, the article shows how to bring the existing instance of the application to the foreground when the user tries to start another instance. That's a very nice touch that the other Mutex solutions described here do not address.

其次,本文展示了如何在用户尝试启动另一个实例时将应用程序的现有实例置于前台。这是这里描述的其他互斥解决方案没有解决的非常好的接触。



UPDATE

更新

As of 8/1/2014, the article I linked to above is still active, but the blog hasn't been updated in a while. That makes me worry that eventually it might disappear, and with it, the advocated solution. I'm reproducing the content of the article here for posterity. The words belong solely to the blog owner at Sanity Free Coding.

截至 2014 年 8 月 1 日,我上面链接的文章仍然有效,但博客已经有一段时间没有更新了。这让我担心它最终可能会消失,随之而来的是提倡的解决方案。我在这里复制文章的内容以供后人使用。这些词完全属于Sanity Free Coding的博客所有者。

Today I wanted to refactor some code that prohibited my application from running multiple instances of itself.

Previously I had use System.Diagnostics.Processto 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:

今天我想重构一些禁止我的应用程序运行多个自身实例的代码。

以前我使用System.Diagnostics.Process在进程列表中搜索 myapp.exe 的一个实例。虽然这有效,但它带来了很多开销,我想要一些更干净的东西。

知道我可以为此使用互斥锁(但以前从未这样做过),我开始减少我的代码并简化我的生活。

在我的应用程序 main 类中,我创建了一个名为Mutex的静态:

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.WaitOnehas 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:

拥有一个命名的互斥锁允许我们跨多个线程和进程堆栈同步,这正是我正在寻找的魔法。

Mutex.WaitOne有一个重载,它指定了我们等待的时间量。由于我们实际上并不想同步我们的代码(更多只是检查它是否当前正在使用),我们使用带有两个参数的重载:Mutex.WaitOne(Timespan timeout, bool exitContext)。如果可以进入则等待返回 true,否则返回 false。在这种情况下,我们根本不想等待;如果我们的互斥锁正在被使用,跳过它并继续,所以我们传入 TimeSpan.Zero(等待 0 毫秒),并将 exitContext 设置为 true,这样我们就可以在尝试获取锁定之前退出同步上下文。使用它,我们将 Application.Run 代码包装在如下内容中:

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 PostMessageto broadcast a custom message to every window (the custom message was registered with RegisterWindowMessageby 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 WndProcin 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

因此,如果我们的应用程序正在运行,WaitOne 将返回 false,我们将收到一个消息框。

我没有显示消息框,而是选择使用一个小 Win32 来通知我正在运行的实例有人忘记它已经在运行(通过将自己带到所有其他窗口的顶部)。为了实现这一点,我使用PostMessage将自定义消息广播到每个窗口(自定义消息是 由我正在运行的应用程序向RegisterWindowMessage 注册的,这意味着只有我的应用程序知道它是什么)然后我的第二个实例退出。正在运行的应用程序实例将接收该通知并对其进行处理。为了做到这一点,我在主窗体中覆盖了WndProc并监听了我的自定义通知。当我收到该通知时,我将表单的 TopMost 属性设置为 true 以将其置于顶部。

这是我最终的结果:

  • 程序.cs
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
  • 本地方法.cs
// 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)
  • Form1.cs(正面部分)
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;
    }
}

回答by jason saldo

From here.

这里开始

A common use for a cross-process Mutex is to ensure that only instance of a program can run at a time. Here's how it's done:

跨进程互斥锁的一个常见用途是确保一次只能运行程序的实例。这是它的完成方式:

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();
    }    
  }    
}

A good feature of Mutex is that if the application terminates without ReleaseMutex first being called, the CLR will release the Mutex automatically.

Mutex 的一个很好的特性是,如果应用程序在没有首先调用 ReleaseMutex 的情况下终止,CLR 将自动释放 Mutex。

回答by Dale Ragan

You could use the Mutex class, but you will soon find out that you will need to implement the code to pass the arguments and such yourself. Well, I learned a trick when programming in WinForms when I read Chris Sell's book. This trick uses logic that is already available to us in the framework. I don't know about you, but when I learn about stuff I can reuse in the framework, that is usually the route I take instead of reinventing the wheel. Unless of course it doesn't do everything I want.

您可以使用 Mutex 类,但您很快就会发现您需要自己实现代码来传递参数等。嗯,当我阅读Chris Sell 的书时,我在 WinForms 中编程时学到了一个技巧。这个技巧使用了我们在框架中已经可用的逻辑。我不了解你,但是当我了解可以在框架中重用的东西时,这通常是我采取的路线,而不是重新发明轮子。当然,除非它不能做我想做的一切。

When I got into WPF, I came up with a way to use that same code, but in a WPF application. This solution should meet your needs based off your question.

当我进入 WPF 时,我想出了一种在 WPF 应用程序中使用相同代码的方法。此解决方案应根据您的问题满足您的需求。

First, we need to create our application class. In this class we are going override the OnStartup event and create a method called Activate, which will be used later.

首先,我们需要创建我们的应用程序类。在这个类中,我们将覆盖 OnStartup 事件并创建一个名为 Activate 的方法,稍后将使用该方法。

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();
    }
}

Second, we will need to create a class that can manage our instances. Before we go through that, we are actually going to reuse some code that is in the Microsoft.VisualBasic assembly. Since, I am using C# in this example, I had to make a reference to the assembly. If you are using VB.NET, you don't have to do anything. The class we are going to use is WindowsFormsApplicationBase and inherit our instance manager off of it and then leverage properties and events to handle the single instancing.

其次,我们需要创建一个可以管理我们的实例的类。在我们开始之前,我们实际上将重用 Microsoft.VisualBasic 程序集中的一些代码。因为我在这个例子中使用了 C#,所以我必须引用程序集。如果您使用的是 VB.NET,则无需执行任何操作。我们将使用的类是 WindowsFormsApplicationBase 并从它继承我们的实例管理器,然后利用属性和事件来处理单个实例化。

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();
    }
}

Basically, we are using the VB bits to detect single instance's and process accordingly. OnStartup will be fired when the first instance loads. OnStartupNextInstance is fired when the application is re-run again. As you can see, I can get to what was passed on the command line through the event arguments. I set the value to an instance field. You could parse the command line here, or you could pass it to your application through the constructor and the call to the Activate method.

基本上,我们使用 VB 位来检测单个实例并进行相应处理。OnStartup 将在第一个实例加载时触发。再次重新运行应用程序时会触发 OnStartupNextInstance。如您所见,我可以通过事件参数获取在命令行上传递的内容。我将该值设置为实例字段。您可以在此处解析命令行,也可以通过构造函数和对 Activate 方法的调用将其传递给您的应用程序。

Third, it's time to create our EntryPoint. Instead of newing up the application like you would normally do, we are going to take advantage of our SingleInstanceManager.

第三,是时候创建我们的入口点了。我们将利用我们的 SingleInstanceManager,而不是像通常那样更新应用程序。

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

Well, I hope you are able to follow everything and be able use this implementation and make it your own.

好吧,我希望您能够遵循所有内容并能够使用此实现并使其成为您自己的。

回答by Matt Davison

You should never use a named mutex to implement a single-instance application (or at least not for production code). Malicious code can easily DoS (Denial of Service) your ass...

您永远不应该使用命名互斥锁来实现单实例应用程序(或至少不要用于生产代码)。恶意代码很容易 DoS(拒绝服务)你的屁股...

回答by Bruce

Just some thoughts: There are cases when requiring that only one instance of an application is not "lame" as some would have you believe. Database apps, etc. are an order of magnitude more difficult if one allows multiple instances of the app for a single user to access a database (you know, all that updating all the records that are open in multiple instances of the app on the users machine, etc.). First, for the "name collision thing, don't use a human readable name - use a GUID instead or, even better a GUID + the human readable name. Chances of name collision just dropped off the radar and the Mutex doesn't care. As someone pointed out, a DOS attack would suck, but if the malicious person has gone to the trouble of getting the mutex name and incorporating it into their app, you are pretty much a target anyway and will have to do MUCH more to protect yourself than just fiddle a mutex name. Also, if one uses the variant of: new Mutex(true, "some GUID plus Name", out AIsFirstInstance), you already have your indicator as to whether or not the Mutex is the first instance.

只是一些想法:在某些情况下,要求只有一个应用程序实例并不像您认为的那样“蹩脚”。如果允许单个用户访问应用程序的多个实例,则数据库应用程序等的难度要高一个数量级(您知道,所有这些都更新在用户的应用程序的多个实例中打开的所有记录)机等)。首先,对于“名称冲突的事情,不要使用人类可读的名称 - 使用 GUID 代替,或者甚至更好的 GUID + 人类可读的名称。名称冲突的机会刚刚从雷达上消失,互斥体不在乎. 正如有人指出的那样,DOS 攻击会很糟糕,但是如果恶意的人不惜代价获取互斥锁名称并将其合并到他们的应用程序中,无论如何,您几乎是一个目标,并且必须做更多的事情来保护自己,而不仅仅是摆弄互斥锁名称。此外,如果使用以下变体:new Mutex(true, "some GUID plus Name", out AIsFirstInstance),您已经有了关于 Mutex 是否是第一个实例的指标。

回答by Oliver Friedrich

Well, I have a disposable Class for this that works easily for most use cases:

好吧,我有一个一次性类,它可以在大多数用例中轻松工作:

Use it like this:

像这样使用它:

static void Main()
{
    using (SingleInstanceMutex sim = new SingleInstanceMutex())
    {
        if (sim.IsOtherInstanceRunning)
        {
            Application.Exit();
        }

        // Initialize program here.
    }
}

Here it is:

这里是:

/// <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
}

回答by Peter

So many answers to such a seemingly simple question. Just to shake things up a little bit here is my solution to this problem.

这么一个看似简单的问题,竟然有这么多答案。在这里稍微改变一下是我对这个问题的解决方案。

Creating a Mutex can be troublesome because the JIT-er only sees you using it for a small portion of your code and wants to mark it as ready for garbage collection. It pretty much wants to out-smart you thinking you are not going to be using that Mutex for that long. In reality you want to hang onto this Mutex for as long as your application is running. The best way to tell the garbage collector to leave you Mutex alone is to tell it to keep it alive though out the different generations of garage collection. Example:

创建互斥锁可能会很麻烦,因为 JIT-er 只会看到您在一小部分代码中使用它,并希望将其标记为准备好进行垃圾回收。它非常想超越你认为你不会使用那个 Mutex 这么久的聪明。实际上,只要您的应用程序正在运行,您就希望一直使用这个互斥锁。告诉垃圾收集器不要让您单独使用互斥锁的最好方法是告诉它在不同代的车库收集中保持它的存在。例子:

var m = new Mutex(...);
...
GC.KeepAlive(m);

I lifted the idea from this page: http://www.ai.uga.edu/~mc/SingleInstance.html

我从这个页面提出了这个想法:http: //www.ai.uga.edu/~mc/SingleInstance.html

回答by huseyint

A new one that uses Mutex and IPC stuff, and also passes any command line arguments to the running instance, is WPF Single Instance Application.

一个新的使用互斥和 IPC 的东西,并将任何命令行参数传递给正在运行的实例,是WPF Single Instance Application

回答by Sergey Aldoukhov

Here is what I use. It combined process enumeration to perform switching and mutex to safeguard from "active clickers":

这是我使用的。它结合了进程枚举来执行切换和互斥以防止“活跃的点击者”:

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();
        }
    }

回答by Simon_Weaver

MSDN actually has a sample application for both C# and VB to do exactly this: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

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.

开发单实例检测的最常见和最可靠的技术是使用 Microsoft .NET Framework 远程处理基础结构 (System.Remoting)。Microsoft .NET Framework(2.0 版)包括一个类型 WindowsFormsApplicationBase,它封装了所需的远程处理功能。要将此类型合并到 WPF 应用程序中,需要从它派生一个类型,并用作应用程序静态入口点方法 Main 和 WPF 应用程序的 Application 类型之间的填充。shim 检测应用程序何时首次启动,以及何时尝试进行后续启动,并产生控制 WPF 应用程序类型以确定如何处理启动。

  • For C# people just take a deep breath and forget about the whole 'I don't wanna include VisualBasic DLL'. Because of thisand what Scott Hanselman saysand the fact that this pretty much is the cleanest solution to the problem and is designed by people who know a lot more about the framework than you do.
  • From a usability standpoint the fact is if your user is loading an application and it is already open and you're giving them an error message like 'Another instance of the app is running. Bye'then they're not gonna be a very happy user. You simply MUST (in a GUI application) switch to that application and pass in the arguments provided - or if command line parameters have no meaning then you must pop up the application which may have been minimized.
  • 对于 C# 来说,人们只需深呼吸并忘记整个“我不想包含 VisualBasic DLL”。正因为如此,以及Scott Hanselman 所说的,以及事实上这几乎是解决问题的最干净的解决方案,并且是由比你更了解框架的人设计的。
  • 从可用性的角度来看,事实是如果您的用户正在加载一个应用程序并且它已经打开并且您给他们一条错误消息,'Another instance of the app is running. Bye'那么他们不会成为一个非常满意的用户。您只需(在 GUI 应用程序中)切换到该应用程序并传入提供的参数 - 或者如果命令行参数没有意义,那么您必须弹出可能已最小化的应用程序。

The framework already has support for this - its just that some idiot named the DLL Microsoft.VisualBasicand it didn't get put into Microsoft.ApplicationUtilsor something like that. Get over it - or open up Reflector.

该框架已经对此提供了支持 - 只是一些白痴命名了 DLLMicrosoft.VisualBasic而它没有被放入Microsoft.ApplicationUtils或类似的东西。克服它 - 或打开反射器。

Tip: If you use this approach exactly as is, and you already have an App.xaml with resources etc. you'll want to take a look at this too.

提示:如果您完全按原样使用此方法,并且您已经拥有一个包含资源等的 App.xaml。您也需要查看此内容