예제 #1
0
        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;
        }