【问题标题】:How to get the z-order in windows?如何在windows中获得z-order?
【发布时间】:2010-10-23 23:02:22
【问题描述】:

我正在制作一个与每个正在运行的应用程序交互的应用程序。现在,我需要一种获取窗口 z 顺序的方法。例如,如果 Firefox 和 notepad 正在运行,我需要知道哪个在前面。

有什么想法吗?除了对每个应用程序的主窗口执行此操作外,我还需要对其子窗口和姐妹窗口(属于同一进程的窗口)执行此操作。

【问题讨论】:

    标签: c# .net windows z-order


    【解决方案1】:

    您可以使用 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

    【讨论】:

    • 并且“桌面”应该能够通过为父窗口指定 null 来用作父窗口。因此,您可以轻松获得桌面上的顶级窗口。
    • 这不可靠。 GetNextWindow 只需调用 GetWindow。来自GetWindow reference:“调用 GetWindow 来执行此任务的应用程序有可能陷入无限循环或引用已销毁窗口的句柄。
    【解决方案2】:

    简洁明了:

    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 顺序都可以同时检索。

    【讨论】:

    • 在比较两个重叠的窗口(如表单和任务栏)时不起作用。当窗口在任务栏上方时,任务栏 z-order 高于提示任务栏在顶部的窗口,而不是在顶部。
    • 其实我改变了主意。 @Erwinus,according to Microsoft 顶级窗口出现在 z 顺序 其他窗口之前,这意味着较小的 z 顺序编号意味着窗口实际上更高。 EnumWindowsGW_HWNDPREVGW_HWNDNEXT 的工作方式证明了这一点。这里的其他答案同意。我不愿意让这个与 Windows API 相悖。
    • 是的,对这种方法有一些疑问。无论如何都要使用您的第一个代码(使用 getWindow(h, GW_HWNDPREV )),但略有不同。当一个窗口位于另一个窗口的顶部时,您可以测试下窗口是否在“列表”中。如果为真,则必须检查窗口的边界以检查它是否真的重叠。
    【解决方案3】:

    这是我的 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;
            }
        }
    }
    

    【讨论】:

      【解决方案4】:
                  // 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);
      

      【讨论】:

        【解决方案5】:

        为了获取 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 的值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-15
          • 1970-01-01
          • 1970-01-01
          • 2019-11-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多