Window z-order in Windows 10

Defining Window z-order “band”

To clarify, in this context the word “band” means group of z-order

Until Windows 8, there was only one band, the ZBID_DESKTOP band, which is the default one when you write an application that creates a new window and when it gains focus it will go to the highest z-order meaning it will go on top of other windows. This is true unless there are topmost windows. As the name says, it will stay on top of other windows. What if there are 2 topmost windows? Well, in this case, the last window to gain focus will stay on top of other one. In the end there are 2 groups of z-order in the ZBID_DESKTOP band, normal and topmost. These will never be “touched” by each other, topmost window will always stay on top of other normal window, no matter what.

Starting from Windows 8, Microsoft “introduced” other bands, and all of them are higher z-orders than the desktop band. For example the start menu is on ZBID_IMMERSIVE_MOGO. Task manager “always on top” is on ZBID_SYSTEM_TOOLS. These are always on top of any window in the ZBID_DESKTOP band, that is, any normal or topmost window created by third parts developers will no longer cover start menu and stuff. These bands cannot mix each other, meaning, for example, that ZBID_DESKTOP will never be on top of ZBID_IMMERSIVE_MOGO, that is, bands have their z-orders too.

Like ZBID_DESKTOP, other bands also can be subdivided in 2 groups: normal and topmost window.

The order of bands are the following, from the lowest to the highest z-order (some ZBID orders are unknown for me ATM):

  • ZBID_DESKTOP
  • ZBID_IMMERSIVE_BACKGROUND
  • ZBID_IMMERSIVE_APPCHROME
  • ZBID_IMMERSIVE_MOGO
  • ZBID_IMMERSIVE_INACTIVEMOBODY
  • ZBID_IMMERSIVE_NOTIFICATION
  • ZBID_IMMERSIVE_EDGY
  • ZBID_SYSTEM_TOOLS
  • ZBID_LOCK (Windows 10 only)
  • ZBID_ABOVELOCK_UX (Windows 10 only)
  • ZBID_IMMERSIVE_IHM
  • ZBID_GENUINE_WINDOWS
  • ZBID_UIACCESS

unordered band z-order

  • ZBID_IMMERSIVE_INACTIVEDOCK
  • ZBID_IMMERSIVE_ACTIVEMOBODY
  • ZBID_IMMERSIVE_ACTIVEDOCK
  • ZBID_IMMERSIVE_RESTRICTED (UNUSED)

Visualizing Window bands

ZBID_DESKTOP band, this is where all our window stays on.

ZBID_DESKTOP: this band is where all our window stays on. Let’s suppose you have 2 window here, Paint and Photos. Both are desktop windows, and they can overlap each other by just focusing a window or another. Now suppose that Paint window is topmost, now it will stay on top of Photos, regardless of its focus state.


ZBID_IMMERSIVE_MOGO, band used by the start menu

ZBID_IMMERSIVE_MOGO: this band is used by the start menu AND the taskbar (only if the start menu is open).


ZBID_IMMERSIVE_NOTIFICATIONS

ZBID_IMMERSIVE_NOTIFICATIONS: used by the Action Center, notifications and some system flyouts (e.g. Network, Volume).


ZBID_SYSTEM_TOOLS

ZBID_SYSTEM_TOOLS: used by Task Manager with “Always on Top” enabled, Alt-Tab view.


ZBID_ABOVELOCK_UX

ZBID_ABOVELOCK_UX: used by “Playing Now”. This band and anything else higher will stay on top of the lock screen.


Since posting all ZBIDs would make this post XXL….

  • ZBID_IMMERSIVE_SEARCH: Cortana/Windows Search
  • ZBID_IMMERSIVE_INACTIVEMOBODY: Picture in picture (UWP CompactOverlay)
  • ZBID_IMMERSIVE_APPCHROME: TaskView
  • ZBID_GENUINE_WINDOWS: “Activate Windows”!

Technical Parts

ZBID enum

Windows 8.x / Windows 10

enum ZBID
{
	ZBID_DEFAULT = 0,
	ZBID_DESKTOP = 1,
	ZBID_UIACCESS = 2,
	ZBID_IMMERSIVE_IHM = 3,
	ZBID_IMMERSIVE_NOTIFICATION = 4,
	ZBID_IMMERSIVE_APPCHROME = 5,
	ZBID_IMMERSIVE_MOGO = 6,
	ZBID_IMMERSIVE_EDGY = 7,
	ZBID_IMMERSIVE_INACTIVEMOBODY = 8,
	ZBID_IMMERSIVE_INACTIVEDOCK = 9,
	ZBID_IMMERSIVE_ACTIVEMOBODY = 10,
	ZBID_IMMERSIVE_ACTIVEDOCK = 11,
	ZBID_IMMERSIVE_BACKGROUND = 12,
	ZBID_IMMERSIVE_SEARCH = 13,
	ZBID_GENUINE_WINDOWS = 14,
	ZBID_IMMERSIVE_RESTRICTED = 15,
	ZBID_SYSTEM_TOOLS = 16,

	//Windows 10+
	ZBID_LOCK = 17,
	ZBID_ABOVELOCK_UX = 18,
};

CreateWindowInBand

This is a private api function found in user32.dll. Since it’s not present in Windows SDK headers/libs, you have to use GetProcAddress to get access to that function.

CreateWindowInBand function is the same as CreateWindowEx except it has 1 more parameter, dwBand, that is where you specify the band on which the window should stay (ZBID).

HWND WINAPI CreateWindowInBand(
DWORD dwExStyle,
LPCWSTR lpClassName,
LPCWSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam,
DWORD dwBand);

CreateWindowInBandEx

Private api function found in user32.dll.

Same as CreateWindowInBand plus 1 parameter, dwTypeFlags

HWND WINAPI CreateWindowInBandEx(
DWORD dwExStyle,
LPCWSTR lpClassName,
LPCWSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam,
DWORD dwBand,
DWORD dwTypeFlags);

SetWindowBand

Private api function found in user32.dll.

SetWindowBand function has 3 parameters, hWnd, hWndInserAfter and dwBand. Same as 2 first parameters from SetWindowPos. The returned value indicates success or failure. In case of failure call GetLastError. I’m 99.9% sure you got a nice 0x5 (ACCESS DENIED)

BOOL WINAPI SetWindowBand(
HWND hWnd, 
HWND hwndInsertAfter, 
DWORD dwBand);

GetWindowBand

Private api function found in user32.dll.

GetWindowBand function has 2 parameters, hWnd and pdwBand. pdwBand is a pointer that receives the band (ZBID) of the HWND. The returned value indicates success or failure. In case of failure call GetLastError.

BOOL WINAPI GetWindowBand(
HWND hWnd, 
PDWORD pdwBand);

Can I call these APIs?

Short reply: Some yes (under certain conditions), some no.

  • GetWindowBand works in every case, you can use it without any problem.
  • CreateWindowInBand/Ex works ONLY if you pass ZBID_DEFAULT or ZBID_DESKTOP as dwBand argument. Also ZBID_UIACCESS is permitted only if the process has UIAccess token (obtainable, for example, by setting uiAccess=true in app.manifest, more info here). Any other ZBID will fail with 0x5 (ACCESS DENIED).
  • SetWindowBand will never work, it will always fail with 0x5 (ACCESS DENIED).

Why I can’t call using other ZBIDs?

Because Microsoft added extra checks for these bands.

For CreateWindowInBand/Ex, to be able to use more ZBIDs, the program must have a special PE header, named “.imrsiv” ( bss_seg), flagged with IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY and be signed with a Microsoft certificate “Microsoft Windows”.

For SetWindowBand, well… currently I don’t know how this works internally, but it has extra checks. Explorer calls it via twinui.pcshell.dll

NtUserSetWindowBand breakpoint in WinDbg

This function lets change the band of a window and, for Windows Explorer’s case, bring the taskbar from ZBID_DESKTOP to ZBID_IMMERSIVE_MOGO (and vice versa).

Is there a way to bypass it? You did it, so I guess yes?

Yes…ish. You will not like the way I did it but that’s (currently) the only way.

  • To use CreateWindowInBand with any ZBID, you need to inject a dll into an immersive process. In my case I used RuntimeBroker (explorer.exe would crash easily). An code example can be found here: launcher (to inject a dll) and the dll
CreateWindowInBand via dll-injected immersive process (RuntimeBroker.exe)
  • To use SetWindowBand, you need to inject a dll into explorer.exe and hook SetWindowBand (yep, a trampoline). As soon as you press the Start button the hooked function will be called and you can call the original function with a different hwnd target. This sucks but oh, I haven’t found yet a better way.
SetWindowBand from a dll-injected explorer.exe, called by pressing the start menu button

Conclusion

Starting from Windows 8 Microsoft “introduced” the concept of band, a group of z-order that they will never “touch” each other. There are many bands but for developers, only ZBID_DESKTOP (default band for CreateWindow/Ex) is accessible.

11 thoughts on “Window z-order in Windows 10”

  1. So useful info! Thank you. I found this function like 3 years ago but I couldn’t make it work, tried calling but it always failed then after some research I thought only signed apps can do that and gave up 😀 I should have tried injection nice work.

    Reply
    • I found a better way to call SetWindowBand that doesn’t require hooking. You need to call NtUserEnableIAMAccess before calling setwindowband, then it works.

      Reply
      • Are you sure? Because I did try that without success. NtUserEnableIAMAccess requires a uint64 “key” as param, and that key is tied to an unique process that acquired it. Maybe you found a way to make it work (?)

        Reply
  2. Very interesting.
    But how does work on-screen keyboard osk.exe?
    It has ZBID_UIACCESS but is in front of any window.
    Even FullScreen DirectX programs do not run when osk running.

    Reply
      • Thank You!
        Dont You know, what dwTypeFlags mean and what is its enumeration in CreateWindowInBandEx?
        For me it works only dwTypeFlags=0 or dwTypeFlags=4.
        If dwTypeFlags>=8, I get ERROR_INVALID_PARAMETER.

        Reply
  3. Thank you very much. It helps me a lot.
    Recently, I have been working on a desktop watermarking function, which needs to be displayed on top of other Windows. However, I have a problem: I cannot cover the full screen player window. Do you know why?

    Reply

Leave a Comment

Enable Notifications    Ok No thanks