简体   繁体   中英

How can I enforce a single instance of my application?

How can I ensure a single instance of my application and to set the focus to it when attempting to open a second instance?

I tried:

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

This is not focusing the application. How can I fix this?

You may want to use Mutex instead, which avoids to search for windows in a slightly unreliable way (imagine you rename your main form or open another form instead).

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

It looks like you're passing Text as a parameter to your FocusWindow method, but while doing a Contains check. I'll bet the Text is only a partial window title and thus, causing FindWindow to fail. Try passing the full text of the window handle instead like:

var proc = Process.GetProcesses()
                  .Where(p => p.MainWindowTitle.Contains(Text))
                  .FirstOrDefault();

        if (proc != null)
        {
            FocusWindow(p.MainWindowTitle);
            Application.Exit();
        }

它可能是由windows的相同标题引起的,因此FindWindow获取实际的windows句柄,尝试使用EnumWindows函数而不是FindWindow。

Doing this check on form load is incorrect. You should use a Mutex to make sure only one instance of the application is running. See this article for an example on how to do that and also setting focus to the existing instance.

Place this code in your App.xaml.cs file:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM