【发布时间】:2010-10-23 23:02:22
【问题描述】:
我正在制作一个与每个正在运行的应用程序交互的应用程序。现在,我需要一种获取窗口 z 顺序的方法。例如,如果 Firefox 和 notepad 正在运行,我需要知道哪个在前面。
有什么想法吗?除了对每个应用程序的主窗口执行此操作外,我还需要对其子窗口和姐妹窗口(属于同一进程的窗口)执行此操作。
【问题讨论】:
我正在制作一个与每个正在运行的应用程序交互的应用程序。现在,我需要一种获取窗口 z 顺序的方法。例如,如果 Firefox 和 notepad 正在运行,我需要知道哪个在前面。
有什么想法吗?除了对每个应用程序的主窗口执行此操作外,我还需要对其子窗口和姐妹窗口(属于同一进程的窗口)执行此操作。
【问题讨论】:
您可以使用 GetTopWindow 函数搜索父窗口的所有子窗口,并返回 z 顺序最高的子窗口句柄。 GetNextWindow 函数按 z 顺序检索下一个或上一个窗口的句柄。
GetTopWindow:http://msdn.microsoft.com/en-us/library/ms633514(VS.85).aspx
获取下一个窗口:http://msdn.microsoft.com/en-us/library/ms633509(VS.85).aspx
【讨论】:
GetNextWindow 只需调用 GetWindow。来自GetWindow reference:“调用 GetWindow 来执行此任务的应用程序有可能陷入无限循环或引用已销毁窗口的句柄。”
简洁明了:
int GetZOrder(IntPtr hWnd)
{
var z = 0;
for (var h = hWnd; h != IntPtr.Zero; h = GetWindow(h, GW.HWNDPREV)) z++;
return z;
}
如果您需要更高的可靠性:
/// <summary>
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
/// </summary>
int[] GetZOrder(params IntPtr[] hWnds)
{
var z = new int[hWnds.Length];
for (var i = 0; i < hWnds.Length; i++) z[i] = -1;
var index = 0;
var numRemaining = hWnds.Length;
EnumWindows((wnd, param) =>
{
var searchIndex = Array.IndexOf(hWnds, wnd);
if (searchIndex != -1)
{
z[searchIndex] = index;
numRemaining--;
if (numRemaining == 0) return false;
}
index++;
return true;
}, IntPtr.Zero);
return z;
}
(根据GetWindow 的备注部分,EnumChildWindows 比在循环中调用GetWindow 更安全,因为您的GetWindow 循环对于外部更改不是原子的。根据EnumChildWindows 的参数部分,使用 null 父级调用等效于 EnumWindows。)
然后,不是为每个窗口单独调用EnumWindows,这也不是原子的并且不受并发更改的影响,而是将要比较的每个窗口发送到一个参数数组中,以便它们的 z 顺序都可以同时检索。
【讨论】:
EnumWindows 和 GW_HWNDPREV 和 GW_HWNDNEXT 的工作方式证明了这一点。这里的其他答案同意。我不愿意让这个与 Windows API 相悖。
这是我的 C# 解决方案: 该函数返回给定 HWND 的兄弟节点之间的 zIndex,从 0 开始表示最低 zOrder。
using System;
using System.Runtime.InteropServices;
namespace Win32
{
public static class HwndHelper
{
[DllImport("user32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
public static bool GetWindowZOrder(IntPtr hwnd, out int zOrder)
{
const uint GW_HWNDPREV = 3;
const uint GW_HWNDLAST = 1;
var lowestHwnd = GetWindow(hwnd, GW_HWNDLAST);
var z = 0;
var hwndTmp = lowestHwnd;
while (hwndTmp != IntPtr.Zero)
{
if (hwnd == hwndTmp)
{
zOrder = z;
return true;
}
hwndTmp = GetWindow(hwndTmp, GW_HWNDPREV);
z++;
}
zOrder = int.MinValue;
return false;
}
}
}
【讨论】:
// Find z-order for window.
Process[] procs = Process.GetProcessesByName("notepad");
Process top = null;
int topz = int.MaxValue;
foreach (Process p in procs)
{
IntPtr handle = p.MainWindowHandle;
int z = 0;
do
{
z++;
handle = GetWindow(handle, 3);
} while(handle != IntPtr.Zero);
if (z < topz)
{
top = p;
topz = z;
}
}
if(top != null)
Debug.WriteLine(top.MainWindowTitle);
【讨论】:
为了获取 Z-Order 实现这个函数(通过使用GetWindow Windows API 函数)
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
static int GetWindowZOrder(IntPtr hWnd)
{
var zOrder = -1;
while ((hWnd = GetWindow(hWnd, 2 /* GW_HWNDNEXT */)) != IntPtr.Zero) zOrder++;
return zOrder;
}
返回值是一个从零开始的索引。返回 -1 表示无效的 hWnd。
方法是通过窗户到顶部。总爬升次数就是 Z-Order 的值。
【讨论】: