Enumerar janelas como o alt-tab faz

Estou criando uma substituição de alt-tab para o Vista, mas tenho alguns problemas listando todos os programas ativos.

Estou usando o EnumWindows para obter uma lista do Windows, mas essa lista é enorme. Ele contém cerca de 400 itens quando eu só tenho 10 janelas abertas. Parece ser um hwnd para cada controle e muitas outras coisas.

Então eu tenho que filtrar essa lista de alguma forma, mas não consigo fazer exatamente como o alt-tab faz.

Este é o código que eu uso para filtrar a lista agora. Ele funciona muito bem, mas eu recebo algumas janelas indesejadas como janelas de ferramentas desanexadas no Visual Studio e também sinto falta de janelas como o iTunes e o Warcraft3.

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; } 

Raymond Chen respondeu isso há um tempo atrás ( http://blogs.msdn.com/oldnewthing/archive/2007/10/08/5351207.aspx ):

Na verdade, é bem simples, apesar de quase nada que você possa adivinhar sozinho. Nota: Os detalhes desse algoritmo são um detalhe de implementação. Pode mudar a qualquer momento, então não confie nele. Na verdade, já mudou com Flip e Flip3D; Estou falando apenas da janela Clássico Alt + Tab aqui.

Para cada janela visível, suba a cadeia de proprietários até encontrar o proprietário da raiz. Em seguida, desça a última cadeia de pop-ups ativa visível até encontrar uma janela visível. Se você está de volta ao ponto de partida, coloque a janela na lista Alt + Tab. No pseudo-código:

 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; } 

Siga o link para a input do blog de Chen para mais detalhes e algumas condições de canto.

Obrigado Mike B. O exemplo do blog de Raymonds apontou-me na direção correta.

No entanto, existem algumas exceções que precisam ser feitas, o Windows Live Messenger tem vários hacks para criar sombras sob o windows, etc.

Aqui está o meu código completo, já o usei por um dia e não notei nenhuma diferença na aba alt real. Há algum código subjacente não postado, mas não há problema em descobrir o que ele faz. 🙂

  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); }