public void HandleNCCalcsize(IntPtr wParam, IntPtr lParam) { // lParam is an [in, out] that can be either a RECT* (wParam == FALSE) or an NCCALCSIZE_PARAMS*. // Since the first field of NCCALCSIZE_PARAMS is a RECT and is the only field we care about // we can unconditionally treat it as a RECT. /* DefWindowProc must be called in both the maximized and non-maximized * cases, otherwise tile/cascade windows won't work */ var nonclient = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); DefWindowProcW(_framelessInfo.Handle, WM.NCCALCSIZE, wParam, lParam); var client = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); if (IsMaximized(_framelessInfo.Handle)) { WINDOWINFO wi = new WINDOWINFO(null); GetWindowInfo(_framelessInfo.Handle, ref wi); /* Maximized windows always have a non-client border that hangs over * the edge of the screen, so the size proposed by WM_NCCALCSIZE is * fine. Just adjust the top border to remove the window title. */ var rect = new RECT(); rect.left = client.left; rect.top = (int)(nonclient.top + wi.cyWindowBorders); rect.right = client.right; rect.bottom = client.bottom; IntPtr mon = MonitorFromWindow(_framelessInfo.Handle, MONITOR.DEFAULTTOPRIMARY); MONITORINFOEXW mi = new MONITORINFOEXW(null); GetMonitorInfoW(mon, ref mi); /* If the client rectangle is the same as the monitor's rectangle, * the shell assumes that the window has gone fullscreen, so it removes * the topmost attribute from any auto-hide appbars, making them * inaccessible. To avoid this, reduce the size of the client area by * one pixel on a certain edge. The edge is chosen based on which side * of the monitor is likely to contain an auto-hide appbar, so the * missing client area is covered by it. */ if (rect.AreEqual(mi.rcMonitor)) { if (HasAutohideAppbar(ABE_BOTTOM, mi.rcMonitor)) { rect.bottom--; } else if (HasAutohideAppbar(ABE_LEFT, mi.rcMonitor)) { rect.left++; } else if (HasAutohideAppbar(ABE_TOP, mi.rcMonitor)) { rect.top++; } else if (HasAutohideAppbar(ABE_RIGHT, mi.rcMonitor)) { rect.right--; } } Marshal.StructureToPtr(rect, lParam, false); } else { /* For the non-maximized case, set the output RECT to what it was * before WM_NCCALCSIZE modified it. This will make the client size the * same as the non-client size. */ Marshal.StructureToPtr(nonclient, lParam, false); } }