The Windows API does not provide a simple function to create a list of all top-level windows according to the Alt-Tab list. The EnumWindows function provides many more windows, as most programs have multiple windows. But at most one of them belongs in the Alt-Tab list. To find the right one, you have to go down the owner chain for each window until you find the root owner. If the tested window equals the LastActivePopup it additionally must meet certain criteria to be included in the Alt-Tab list. Thus you can create a list of window handles appearing in the Alt-Tab list and sorted by Z-order.
The following code demonstrates the procedure for C-Sharp. In this example the loop breaks as soon as the last active window is found. exclHwnd may be the handle of the form (this.Handle = ActiveHwnd) that uses the code.
private static ArrayList windowHandles = new ArrayList();
public static IntPtr GetLastWinHandle(IntPtr exclHwnd)
{
IntPtr indexHwnd = IntPtr.Zero;
IntPtr ownerHwnd = IntPtr.Zero;
IntPtr foundHwnd = IntPtr.Zero;
windowHandles.Clear();
EnumWindows(new EnumWindowsCallback(EnumCallback), 0);
foreach (int i in windowHandles)
{
indexHwnd = new IntPtr(i);
if (IsWindowVisible(indexHwnd) && indexHwnd != exclHwnd)
{
ownerHwnd = indexHwnd;
do
{
ownerHwnd = GetWindow(ownerHwnd, GW_OWNER);
} while (!IntPtr.Zero.Equals(GetWindow(ownerHwnd, GW_OWNER)));
ownerHwnd = ownerHwnd != IntPtr.Zero ? ownerHwnd : indexHwnd;
if (GetLastActivePopup(ownerHwnd) == indexHwnd)
{
int es = GetWindowLongPtr(indexHwnd, GWL_EXSTYLE);
if ((!(((es & Win32andMore.WS_EX_TOOLWINDOW) == Win32andMore.WS_EX_TOOLWINDOW) && ((es & Win32andMore.WS_EX_APPWINDOW) != Win32andMore.WS_EX_APPWINDOW))) && !IsInvisibleWin10BackgroundAppWindow(indexHwnd))
{
foundHwnd = indexHwnd;
break;
}
}
}
}
return foundHwnd;
}
private static bool EnumCallback(int hWnd, int lParam)
{
windowHandles.Add(hWnd);
return true;
}
private bool IsInvisibleWin10BackgroundAppWindow(IntPtr hWindow)
{
int cloakedVal;
int hr = Win32andMore.DwmGetWindowAttribute(hWindow, Win32andMore.DWMWA_CLOAKED, out cloakedVal, sizeof(int));
if (hr != 0) // returns S_OK (which is zero) on success. Otherwise, it returns an HRESULT error code
{
cloakedVal = 0;
}
return cloakedVal != 0 ? true : false;
}
Downloads
This demo application shows all window titles in a listview and allows to toggle between the last two windows by tapping the Right Windows Key. Read the „WinCycle.Readme.txt“ if you want to use a different key.
- Source code (C#, VS2010, .NET ≥ 2.0): WinCycle_Source (107 KB)
- Stand alone program (Windows executable): WinCycle_Binary (45,4 KB)
License
This article, along with any associated source code and files, is licensed under the BSD License.