4

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?

Kamil Budziewski
  • 22,699
  • 14
  • 85
  • 105
Jack
  • 16,276
  • 55
  • 159
  • 284

5 Answers5

6

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);
    }
}
Arseni Mourzenko
  • 50,338
  • 35
  • 112
  • 199
2

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();
        }
Steve Danner
  • 21,818
  • 7
  • 41
  • 51
1

It's probably caused by the same title of the windows, so the FindWindow get the actual windows handle, try to use the EnumWindows function instead of FindWindow.

pistipanko
  • 745
  • 5
  • 9
1

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.

Adi Lester
  • 24,731
  • 12
  • 95
  • 110
0

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
NickV
  • 549
  • 7
  • 17