private static IntPtr KeepFullScreenHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { //处理WM_WINDOWPOSCHANGING消息 const int WINDOWPOSCHANGING = 0x0046; if (msg == WINDOWPOSCHANGING) { try { //得到WINDOWPOS结构体 var pos = (InteropValues.WindowPosition) Marshal.PtrToStructure(lParam, typeof(InteropValues.WindowPosition)); if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOMOVE) != 0 && (pos.Flags & InteropValues.WindowPositionFlags.SWP_NOSIZE) != 0) { //既然你既不改变位置,也不改变尺寸,我就不管了... return IntPtr.Zero; } if (InteropMethods.IsIconic(hwnd)) { // 如果在全屏期间最小化了窗口,那么忽略后续的位置调整。 // 否则按后续逻辑,会根据窗口在 -32000 的位置,计算出错误的目标位置,然后就跳到主屏了。 return IntPtr.Zero; } //获取窗口现在的矩形,下面用来参考计算目标矩形 if (InteropMethods.GetWindowRect(hwnd, out var rect)) { var targetRect = rect; //窗口想要变化的目标矩形 if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOMOVE) == 0) { //需要移动 targetRect.Left = pos.X; targetRect.Top = pos.Y; } if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOSIZE) == 0) { //要改变尺寸 targetRect.Right = targetRect.Left + pos.Width; targetRect.Bottom = targetRect.Top + pos.Height; } else { //不改变尺寸 targetRect.Right = targetRect.Left + rect.Width; targetRect.Bottom = targetRect.Top + rect.Height; } //使用目标矩形获取显示器信息 var monitor = InteropMethods.MonitorFromRect(ref targetRect, InteropValues.MONITOR_DEFAULTTOPRIMARY); var info = new InteropValues.MONITORINFO(); info.cbSize = (uint) Marshal.SizeOf(info); if (InteropMethods.GetMonitorInfo(monitor, ref info)) { //基于显示器信息设置窗口尺寸位置 pos.X = info.rcMonitor.Left; pos.Y = info.rcMonitor.Top; pos.Width = info.rcMonitor.Right - info.rcMonitor.Left; pos.Height = info.rcMonitor.Bottom - info.rcMonitor.Top; pos.Flags &= ~(InteropValues.WindowPositionFlags.SWP_NOSIZE | InteropValues.WindowPositionFlags.SWP_NOMOVE | InteropValues.WindowPositionFlags.SWP_NOREDRAW); pos.Flags |= InteropValues.WindowPositionFlags.SWP_NOCOPYBITS; if (rect == info.rcMonitor) { var hwndSource = HwndSource.FromHwnd(hwnd); if (hwndSource?.RootVisual is Window window) { //确保窗口的 WPF 属性与 Win32 位置一致,防止有逗比全屏后改 WPF 的属性,发生一些诡异的行为 //下面这样做其实不太好,会再次触发 WM_WINDOWPOSCHANGING 来着.....但是又没有其他时机了 // WM_WINDOWPOSCHANGED 不能用 //(例如:在进入全屏后,修改 Left 属性,会进入 WM_WINDOWPOSCHANGING,然后在这里将消息里的结构体中的 Left 改回, // 使对 Left 的修改无效,那么将不会进入 WM_WINDOWPOSCHANGED,窗口尺寸正常,但窗口的 Left 属性值错误。) var logicalPos = hwndSource.CompositionTarget.TransformFromDevice.Transform( new Point(pos.X, pos.Y)); var logicalSize = hwndSource.CompositionTarget.TransformFromDevice.Transform( new Point(pos.Width, pos.Height)); window.Left = logicalPos.X; window.Top = logicalPos.Y; window.Width = logicalSize.X; window.Height = logicalSize.Y; } } //将修改后的结构体拷贝回去 Marshal.StructureToPtr(pos, lParam, false); } } } catch { // 这里也不需要日志啥的,只是为了防止上面有逗比逻辑,在消息循环里面炸了 } } return IntPtr.Zero; }