如何获得焦点 window 处理程序

How to get focused window handler

我有一个 WPF 自定义虚拟键盘​​应用程序。我需要独立托管此 WPF 应用程序,用户应该能够使用它为任何 window 应用程序输入值。

如何获取当前关注的应用window?例如:记事本、notepad++ 或任何其他接受输入的 window 应用程序。

我已尝试使用下面的代码来获取当前活动状态 window,但它 returns WPF 虚拟键盘应用程序本身也处于活动状态

var window = System.Windows.Application.Current.Windows.OfType<Window>().SingleOrDefault(w => w.IsActive);

下面的代码工作正常,但是,这需要 windowname 作为 param 来设置 foregroundwindow

public partial class MainWindow : Window{
    
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
    
static extern bool SetForegroundWindow(IntPtr hWnd);    
    
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

 private void button1_Click(object sender, RoutedEventArgs e){
    SetForegroundWindow(FindWindowByCaption(IntPtr.Zero, "Untitled - Notepad"));
    }
}

要获取当前活动的 window 句柄,您可以使用 user32.dll 中的 GetForegroundWindow

在 C# 中它应该类似于

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

或者如果您的应用程序 window 将处于活动状态 window,您可以使用 GetWindow

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);

enum GetWindow_Cmd : uint {
    GW_HWNDFIRST = 0,
    GW_HWNDLAST = 1,
    GW_HWNDNEXT = 2,
    GW_HWNDPREV = 3,
    GW_OWNER = 4,
    GW_CHILD = 5,
    GW_ENABLEDPOPUP = 6
}

因此您可以将自己的 window 句柄作为第一个参数传递,将 GW_HWNDNEXT 作为第二个参数传递。结果,您获得了低于当前 window.

的 window 的句柄

编辑

如 IInspectable 所述,以上内容在 WPF 应用程序中不起作用。

但您可以尝试使用以下 EnumWindows:

// Get own hWnd
WindowInteropHelper windowHwnd = new(this);
IntPtr ownHwnd = windowHwnd.Handle;

// Find target hWnd
IntPtr targetHWnd = IntPtr.Zero;
NativeMethods.EnumWindows((hWnd, lParam) => 
{
  if (hWnd == ownHwnd) { return true; } // Ignore own window
  if (!NativeMethods.IsWindowVisible(hWnd)) { return true; } // Ignore hidden windows
  if (GetClassName(hWnd).Equals("Shell_TrayWnd", StringComparison.OrdinalIgnoreCase)) { return true; } // Ignore taskbar window
  if (string.IsNullOrEmpty(GetWindowTitle(hWnd))) { return true; } // Ignore windows without a title
  targetHWnd = hWnd; // Found target hWnd
  return false; // Stop iterating
}, IntPtr.Zero);

// Used functions

private string GetWindowTitle(IntPtr hWnd)
{
  int textLength = NativeMethods.GetWindowTextLength(hWnd);
  StringBuilder sb = new(textLength + 1);
  NativeMethods.GetWindowText(hWnd, sb, sb.Capacity);
  return sb.ToString();
}

private string GetClassName(IntPtr hWnd)
{
  StringBuilder sb = new(256);
  NativeMethods.GetClassName(hWnd, sb, sb.Capacity);
  return sb.ToString().Trim();
}

internal static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool IsWindowVisible(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    internal static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
    internal delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
}

编辑 2

为确保创建 WPF hWnd,您可以使用 WindowInteropHelper.EnsureHandle() 方法而不是 Handle 属性。如果 WPF window 尚未显示,这很重要。