枚举像alt-tab这样的窗口

时间:2008-10-16 22:21:43

标签: c# windows winapi

我正在为Vista创建一个alt-tab替代品,但我在列出所有活动程序时遇到了一些问题。

我正在使用EnumWindows获取Windows列表,但此列表非常庞大。当我只打开10个窗口时,它包含大约400个项目。对于每一个控件和许多其他东西来说,它似乎都是一个蠢货。

所以我必须以某种方式过滤此列表,但我无法像alt-tab那样完全按照这样做。

这是我现在用来过滤列表的代码。它工作得很好,但我在Visual Studio中得到了一些不需要的窗口,比如独立的工具窗口,我也想念iTunes和魔兽争霸3等窗口。

private bool ShouldWindowBeDisplayed(IntPtr window)
{
    uint windowStyles = Win32.GetWindowLong(window, GWL.GWL_STYLE);

    if (((uint)WindowStyles.WS_VISIBLE & windowStyles) != (uint)WindowStyles.WS_VISIBLE ||
        ((uint)WindowExStyles.WS_EX_APPWINDOW & windowStyles) != (uint)WindowExStyles.WS_EX_APPWINDOW)
    {
        return true;
    }
    return false;
}

4 个答案:

答案 0 :(得分:24)

雷蒙德回答了这一段时间 (https://devblogs.microsoft.com/oldnewthing/20071008-00/?p=24863):

  

实际上它很简单   几乎没有什么你能猜到的   靠自己。注意:详细信息   算法是一种实现   详情。它可以随时改变,所以   不要依赖它。事实上,它已经   用Flip和Flip3D改变了;我只是   谈论经典Alt + Tab   窗口在这里。

     

对于每个可见窗口,向上走   所有者链直到找到根   所有者。然后走回可见的地方   最后一个活跃的弹出链,直到找到   一个可见的窗口。如果你回来了   在你开始的地方,然后把它   Alt + Tab列表中的窗口。在   伪代码:

BOOL IsAltTabWindow(HWND hwnd)
{
 // Start at the root owner
 HWND hwndWalk = GetAncestor(hwnd, GA_ROOTOWNER);

 // See if we are the last active visible popup
 HWND hwndTry;
 while ((hwndTry = GetLastActivePopup(hwndWalk)) != hwndTry) {
  if (IsWindowVisible(hwndTry)) break;
  hwndWalk = hwndTry;
 }
 return hwndWalk == hwnd;
}

点击陈的博客文章链接,了解更多详情和一些角落条件。

答案 1 :(得分:11)

谢谢Mike B. Raymonds博客的例子向我指出了正确的方向。

但是有一些例外情况需要做,Windows Live Messenger在Windows下创建阴影有很多黑客攻击:@

这是我的完整代码,现在已经使用了一天,并没有注意到真正的alt选项卡有任何差异。有一些底层代码没有发布,但是确定它的作用是没有问题的。 :)

    private static bool KeepWindowHandleInAltTabList(IntPtr window)
    {
        if (window == Win32.GetShellWindow())   //Desktop
            return false;

        //http://stackoverflow.com/questions/210504/enumerate-windows-like-alt-tab-does
        //http://blogs.msdn.com/oldnewthing/archive/2007/10/08/5351207.aspx
        //1. For each visible window, walk up its owner chain until you find the root owner. 
        //2. Then walk back down the visible last active popup chain until you find a visible window.
        //3. If you're back to where you're started, (look for exceptions) then put the window in the Alt+Tab list.
        IntPtr root = Win32.GetAncestor(window, Win32.GaFlags.GA_ROOTOWNER);

        if (GetLastVisibleActivePopUpOfWindow(root) == window)
        {
            WindowInformation wi = new WindowInformation(window);

            if (wi.className == "Shell_TrayWnd" ||                          //Windows taskbar
                wi.className == "DV2ControlHost" ||                         //Windows startmenu, if open
                (wi.className == "Button" && wi.windowText == "Start") ||   //Windows startmenu-button.
                wi.className == "MsgrIMEWindowClass" ||                     //Live messenger's notifybox i think
                wi.className == "SysShadow" ||                              //Live messenger's shadow-hack
                wi.className.StartsWith("WMP9MediaBarFlyout"))              //WMP's "now playing" taskbar-toolbar
                return false;

            return true;
        }
        return false;
    }

    private static IntPtr GetLastVisibleActivePopUpOfWindow(IntPtr window)
    {
        IntPtr lastPopUp = Win32.GetLastActivePopup(window);
        if (Win32.IsWindowVisible(lastPopUp))
            return lastPopUp;
        else if (lastPopUp == window)
            return IntPtr.Zero;
        else
            return GetLastVisibleActivePopUpOfWindow(lastPopUp);
    }

答案 2 :(得分:0)

这是pascal / delphi中的函数,您可以轻松地将其转换为C#。

它包括对Windows 10应用程序的支持。


EnumWindows(@ListApps, 0);

function ListApps(LHWindow: HWND; lParam: Pointer): Boolean; stdcall;
var
   LHDesktop: HWND;
   LHParent: HWND;
   LExStyle: DWORD;

   AppClassName: array[0..255] of char;

   Cloaked: Cardinal;

   titlelen: Integer;
   title: String;
begin

  LHDesktop:=GetDesktopWindow;

    GetClassName(LHWindow, AppClassName, 255);
    LHParent:=GetWindowLong(LHWindow,GWL_HWNDPARENT);
    LExStyle:=GetWindowLong(LHWindow,GWL_EXSTYLE);

    if AppClassName = 'ApplicationFrameWindow' then
      DwmGetWindowAttribute(LHWindow, DWMWA_CLOAKED, @cloaked, sizeof(Cardinal))
    else
      cloaked := DWM_NORMAL_APP_NOT_CLOAKED;

    if IsWindowVisible(LHWindow)
    and (AppClassName <> 'Windows.UI.Core.CoreWindow')
    and ( (cloaked = DWM_NOT_CLOAKED) or (cloaked = DWM_NORMAL_APP_NOT_CLOAKED) )
    and ( (LHParent=0) or (LHParent=LHDesktop) )
    and (Application.Handle<>LHWindow)
    and ((LExStyle and WS_EX_TOOLWINDOW = 0) or (LExStyle and WS_EX_APPWINDOW <> 0))
    then
    begin
      titlelen := GetWindowTextLength(LHWindow);
      SetLength(title, titlelen);
      GetWindowText(LHWindow, PChar(title), titlelen + 1);
      { add each to a list }
      But.ListBox1.Items.Add(title);
      { also add each HWND to the list too, later switch using SwitchToThisWindow }
      { ... }
    end;


  Result := True;
end;

答案 3 :(得分:0)

干得好!我的Pascal有点生锈,但是您的解决方案对您很有帮助。我对此并不陌生,因此请原谅我的代码和/或表达方式。答案相对简单。

要列出Alt-Tab Windows列表,您似乎需要三个条件

1)窗口必须可见-使用GetWindowVisible

2)窗口不能是工具栏窗口-使用GetWindowInfo

3)不得隐藏窗口-使用DwmGetWindowAttribute

我不认为您需要查看类名。我认为WS_EX_APPWINDOW标志经常无法通过测试(例如Chrome)-即使与WS_EX_TOOLWINDOW结合使用也是如此。另外...如果您要在顶级枚举Windows,我认为您无需查看父窗口。

    public static bool IsAltTabWindow(IntPtr hWnd)
    {
        const uint WS_EX_TOOLWINDOW = 0x00000080;
        const uint DWMWA_CLOAKED = 14;

        //  It must be a visible Window
        if (!IsWindowVisible(hWnd)) return false;

        //  It must not be a Tool bar window
        WINDOWINFO winInfo = new WINDOWINFO(true);
        GetWindowInfo(hWnd, ref winInfo);            
        if ((winInfo.dwExStyle & WS_EX_TOOLWINDOW) != 0) return false;

        //  It must not be a cloaked window
        uint CloakedVal;
        DwmGetWindowAttribute(hWnd, DWMWA_CLOAKED, out CloakedVal, sizeof(uint));
        return CloakedVal == 0;
    }