4

我正在尝试使用此处概述的方法构建单实例应用程序。

我尝试使用该解决方案的原因是我需要将命令行从第二次尝试启动应用程序传递到第一个实例,这似乎是实现这一目标的最简单方法。

我需要支持的操作系统风格:

  • 视窗 XP SP3
  • 视窗 7 32 位
  • 视窗 7 64 位

我已经让它在所有三个操作系统版本上都可以工作,但是,我有一台装有 Windows 7 32Bit 的机器,它会因 CantStartSingleInstanceException而崩溃。

这是代码:

SingleInstanceController.cs:

using System;
using Microsoft.VisualBasic.ApplicationServices;

namespace SingleInstanceTest
{
  public class SingleInstanceController : WindowsFormsApplicationBase
  {
    public SingleInstanceController()
    {
      IsSingleInstance = true;
    }

    protected override void OnCreateMainForm()
    {
      base.OnCreateMainForm();

      Form1 f = new Form1();
      MainForm = f;

      // process first command line
      f.SetCommandLine(Environment.GetCommandLineArgs());
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
      base.OnStartupNextInstance(eventArgs);

      Form1 f = MainForm as Form1;

      // process subsequent command lines
      f.SetCommandLine(eventArgs.CommandLine);
    }
  }
}

程序.cs:

using System;
using System.Windows.Forms;

namespace SingleInstanceTest
{
  static class Program
  {
    [STAThread]
    static void Main()
    {
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      SingleInstanceController si = new SingleInstanceController();

      // This triggers the crash on one machine when starting the
      // app for the second time
      si.Run(Environment.GetCommandLineArgs());
    }

    static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
    {
      // this is triggered with CantStartSingleInstanceException
      MessageBox.Show(e.ToString(),"ThreadException");
      MessageBox.Show(e.Exception.ToString(), "ThreadException");
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
      MessageBox.Show(e.ToString(), "UnhandledException");
      MessageBox.Show(e.ExceptionObject.ToString(), "UnhandledException");
    }
  }
}

出于测试目的,该表单只是一个包含显示命令行参数的列表框的普通表单。

任何想法为什么这不能在那台机器上工作?搞了这两天,搞不懂。。。

4

1 回答 1

2

我遇到了同样的问题,但我认为它与 Windows 7 或 32 位无关。就我而言,事实证明,这是一个性能问题。不幸的是,我找不到 WindowsFormsApplicationBase 的源代码,但它使用网络与主应用程序通信,因此可能涉及超时。当主应用程序无论如何都必须执行大量网络 I/O 时,这尤其糟糕。当主应用程序没有足够快地响应对 Run 的调用时,将引发此异常。

我通过微调进程、任务和线程来解决这个问题,因此呼叫首先得到应答。

并通过使用互斥锁和适当的 IPC 摆脱 WindowsFormsApplicationBase,我实际上不仅可以选择超时,还可以捕获任何异常!实际上,对于某些类型的 IPC,甚至不需要互斥体。

有关该主题的更多信息,请参阅这篇精彩文章: https ://www.codeproject.com/Articles/1089841/SingleInstance-NET

我选择的两个最肮脏的解决方法:

  1. 捕获异常并在几毫秒后重试。
  2. 经过一些测试,在基础应用程序中生成一个低优先级的新线程似乎是一个好主意(至少在我的场景中是这样)。

    public void SetCommandLineInThread(string[] args) {
        new Thread(() => {
            SetCommandLine(args);
        }) { IsBackground = true, Priority = ThreadPriority.Lowest }.Start();
    }
    

请注意,我会尽快复制命令行参数。

var args = e.CommandLine.ToArray();
于 2018-01-23T05:46:23.683 回答