【问题标题】:How can I enforce a single instance of my application?如何强制执行我的应用程序的单个实例?
【发布时间】:2012-01-13 19:53:16
【问题描述】:

如何确保我的应用程序只有一个实例,并在尝试打开第二个实例时为其设置焦点?

我试过了:

public partial class Form1 : Form {

    [DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
    public static extern
    IntPtr FindWindow(String lpClassName, String lpWindowName);

    [DllImport("USER32.DLL")]
    public static extern
    Boolean SetForegroundWindow(IntPtr hWnd);

    private void Form1_Load(object sender, EventArgs e)
    {
        bool isRunning = Process.GetProcesses()
                                .Where(p => p.MainWindowTitle.Contains(Text))
                                .Count() > 1;

        if (isRunning)
        {
            FocusWindow(Text);
            Application.Exit();
        }
    }

    public static void FocusWindow(string title)
    {
        SetForegroundWindow(FindWindow(null, title));
    }
}

这不是应用程序的焦点。我该如何解决这个问题?

【问题讨论】:

标签: c# .net winapi focus


【解决方案1】:

您可能想改用Mutex,这样可以避免以稍微不可靠的方式搜索窗口(假设您重命名主窗体或打开另一个窗体)。

bool createdNew;
Mutex m = new Mutex(true, "SomeNameHere", out createdNew);

if (!createdNew)
{
    // Application already running. Call it and ask to show it's form.
    IpcClientChannel clientChannel = new IpcClientChannel();
    ChannelServices.RegisterChannel(clientChannel, true);

    RemotingConfiguration.RegisterWellKnownClientType(typeof(ExchangeBase), "ipc://SomeNameHere/YourAppBase");

    ExchangeBase Exchange = new ExchangeBase();
    Exchange.ShowForm();
}
else
{
    IpcServerChannel serverChannel = new IpcServerChannel("SomeNameHere");
    ChannelServices.RegisterChannel(serverChannel, true);
    RemotingConfiguration.RegisterWellKnownServiceType(typeof(ExchangeBase), "YourAppBase", WellKnownObjectMode.SingleCall);

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    MainForm = new FormMain();
    if (!MainForm.StopLoading)
    {
        Application.Run(MainForm);

        // Keep the mutex reference alive until the termination of the program.
        GC.KeepAlive(m);
    }
}

【讨论】:

    【解决方案2】:

    看起来您正在将 Text 作为参数传递给您的 FocusWindow 方法,但同时执行 Contains 检查。我敢打赌 Text 只是部分窗口标题,因此会导致 FindWindow 失败。尝试传递窗口句柄的全文,而不是像:

    var proc = Process.GetProcesses()
                      .Where(p => p.MainWindowTitle.Contains(Text))
                      .FirstOrDefault();
    
            if (proc != null)
            {
                FocusWindow(p.MainWindowTitle);
                Application.Exit();
            }
    

    【讨论】:

      【解决方案3】:

      可能是windows标题相同导致的,所以FindWindow获取实际的windows句柄,尝试使用EnumWindows函数代替FindWindow。

      【讨论】:

        【解决方案4】:

        对表单加载执行此检查是不正确的。您应该使用Mutex 来确保只有一个应用程序实例正在运行。有关如何执行此操作以及将焦点设置到现有实例的示例,请参阅 this article

        【讨论】:

          【解决方案5】:

          将此代码放在您的 App.xaml.cs 文件中:

          using System.Runtime.InteropServices;
          
          #region SetWindowPos Definitions
          [DllImport("user32.dll")]
          static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
              int X, int Y, int cx, int cy, uint uFlags);
          
          static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
          static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
          static readonly IntPtr HWND_TOP = new IntPtr(0);
          static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
          
          const UInt32 SWP_NOSIZE = 0x0001;
          const UInt32 SWP_NOMOVE = 0x0002;
          const UInt32 SWP_NOZORDER = 0x0004;
          const UInt32 SWP_NOREDRAW = 0x0008;
          const UInt32 SWP_NOACTIVATE = 0x0010;
          const UInt32 SWP_FRAMECHANGED = 0x0020;
          const UInt32 SWP_SHOWWINDOW = 0x0040;
          const UInt32 SWP_HIDEWINDOW = 0x0080;
          const UInt32 SWP_NOCOPYBITS = 0x0100;
          const UInt32 SWP_NOOWNERZORDER = 0x0200;
          const UInt32 SWP_NOSENDCHANGING = 0x0400;
          #endregion
          #region OnStartup
          protected override void OnStartup(StartupEventArgs e)
          {
              // Only allow one instance of the application
              Process thisProc = Process.GetCurrentProcess();
              Process[] processes = Process.GetProcessesByName(thisProc.ProcessName);
              if (processes.Length > 1)
              {
                  Application.Current.Shutdown();
          
                  SetWindowPos(processes[1].MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, 
                      SWP_NOSIZE | SWP_NOMOVE);
                  SetWindowPos(processes[1].MainWindowHandle, HWND_NOTOPMOST, 0, 0, 0, 0, 
                      SWP_NOSIZE | SWP_NOMOVE);
              }
              else
              {
                  base.OnStartup(e);
              }
          }
          #endregion
          

          【讨论】:

            猜你喜欢
            • 2018-04-30
            • 1970-01-01
            • 2011-04-28
            • 1970-01-01
            • 2010-09-29
            • 1970-01-01
            • 1970-01-01
            • 2018-04-10
            • 1970-01-01
            相关资源
            最近更新 更多