Win32API使用技巧 -- 置顶应用

Win32提供了SetForegroundWindow方法能够将应用设置到前台并激活,可是在某些场景下,只调用该接口会返回0,即设置失败。好比以下场景:工具

当前前台应用为一个全屏的应用,非前台应用的进程使用Process.Start()方法启动截图工具,尝试使用SetForegroundWindow()将窗口设置到前台,此时会几率性设置失败。ui

尝试了多种方法后,最终使用以下方法解决了该问题:code

public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
public const int SWP_NOSIZE = 0x1;
public const int SWP_NOMOVE = 0x2;

[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hwnd);

[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hwnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);

public void StartSnippingTool()
{
    if (!Envronment.Is64BitProcess)
    {
        Process.Start(@"C:\Windows\sysnative\SnippingTool.exe");
    }
    else
    {
        Process.Start(@"C:\Windows\system32\SnippingTool.exe");
    }

    Thread.Sleep(500);
    IntPtr snippingToolHandle = FindWindow("Microsoft-Windows-SnipperToolbar", "截图工具");
    if (snippingToolHandle != IntPtr.Zero)
    {
        SetForegroundWindowCustom(snippingToolHandle);
    }
}

private void SetForegroundWindowCustom(IntPtr hwnd)
{
    IntPtr currentWindow = GetForegroundWindow();
    if (currentWindow == hwnd)
    {
        Console.WriteLine("应用已在顶层");
        return;
    }

    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
    SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);

    bool result = SetForegroundWindow(hwnd);
    if (result)
    {
        Console.WriteLine("置顶应用成功");
    }
    else
    {
        Console.WriteLine("置顶应用失败");
    }
}