在WndProc中处理AeroSnap消息

7
在我的C# .NET 4应用程序中,我使用 WndProc 处理一些消息,主要涉及将应用程序从全屏切换到非全屏。

目前,我只处理了 SC_MAXIMIZEWM_NCLBUTTONDBLCLK,以确定窗口是被缩放到最大化状态还是从最大化状态缩放(我知道不需要使用 WndProc 处理 SC_MAXIMIZE,但在我双击应用程序的标题栏进行 WM_NCLBUTTONDBLCLK 消息时,Form_Resize 似乎没有触发)。

现在,我注意到如果我通过 Aero Snap 将窗口最大化到屏幕顶部,以上两条消息都不会发送,因此某些逻辑在通过 Aero Snap 最大化窗口时不适用。我只希望在窗口被吸附到屏幕顶部而不是右侧或左侧时,或者窗口从最大化位置取消吸附时处理该消息。

我找不到与 Aero Snap 相关的任何窗口消息。有人知道这些消息的任何参考资料吗?

我也曾经想过这个问题...但是我从来没有弄明白过。 - leviathanbadger
2个回答

6
我猜这里没有特殊的信息;Aero 可能只是使用普通的 Win32 API - ShowWindow(SW_MAXIMIZE) 等等。
要理解 SC_ 消息,就需要知道这些都是来自菜单的请求,要求窗口调整大小/还原等等,但这不是改变窗口大小的唯一机制。可能发生的情况是,当一个窗口获得 SC_MAXIMIZE 时,DefWndProc 通过调用 ShowWindow(SW_MAXIMIZE) 来实现此功能。
你最好监听窗口接收到的 WM_SIZE 消息,无论是由系统菜单、API 还是其他方式触发的大小更改。特别地,lParam 将让您知道窗口是否被最大化 (SIZE_MAXIMIZED) 或还原 (SIZE_RESTORED)。

3
这是正确的。由于Aero Snap使用标准的 WM_MOVING/WM_MOVEWM_SIZING/WM_SIZE 消息,因此不会发送任何特殊通知。如果您在不调用 DefWindowProc 的情况下处理这些消息,则Aero Snap将无法为您的窗口工作。是的,您可以监听 WM_SIZE,但通常最好使用 WM_WINDOWPOSCHANGED。它是一个“新”的函数,在 Windows 3.1 中引入。 :-) 相关阅读 - Cody Gray
1
POSCHANGED 的问题在于它会应用于任何移动/大小更改,包括窗口在“捕捉”之前的移动,因此需要进行更多的筛选。而且我不知道您是否可以从其参数中确定最大化的“捕捉”-没有明显的“窗口已被最大化”的指示器。使用 WM_SIZE,检查 lParam 即可完成! - BrendanMcK
是的,你需要进行一些过滤。这基本上是不可避免的,你必须过滤WM_SIZE以确保你只处理由Aero Snap引发的调整大小事件。关键是你所有的处理代码都在一个地方。我真的看不出在WM_WINDOWPOSCHANGED消息处理程序内部使用switch语句和在窗口过程内部处理WM_MOVEWM_SIZE等的switch语句之间有什么区别。我想伟大的权力伴随着巨大的责任;两者都可以工作。 - Cody Gray
(此外,我的理解是Qu并不只想检测窗口最大化/最小化;他们已经通过SC_MAXIMIZE获取了其他最大化事件,但错过了Aero Snap;因此希望获取所有,包括菜单、双击标题栏以及Aero。) - BrendanMcK
2
这个答案是不正确的,除了 WM_SIZE 可以用来检测新的大小。"Snapped" 和 "maximized" 不是同一件事情:IsZoomed(hwnd) 返回 false。窗口的系统菜单显示最大化和最小化都已启用,但还原没有启用,因为窗口处于正常/恢复状态。WM_SIZE 接收到的是 SIZE_RESTORED,而不是 SIZE_MAXIMIZED。 - Lexikos
显示剩余4条评论

3
这里是处理最大化WM_WINDOWPOSCHANGING消息的代码,而不是WM_SIZE消息。感谢SO上的20多个问题,我不得不阅读所有细节并将它们组合起来使其正常工作。这解决了我在使用不同分辨率的多个显示器时遇到的问题。
//register the hook
public static void WindowInitialized(Window window)
{
    IntPtr handle = (new WindowInteropHelper(window)).Handle;
    var hwndSource = HwndSource.FromHwnd(handle);
    if (hwndSource != null) 
    {
        hwndSource.AddHook(WindowProc);
    }
}

//the important bit
private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case 0x0046: //WINDOWPOSCHANGING
            var winPos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
            var monitorInfo = new MONITORINFO();
            IntPtr monitorContainingApplication = MonitorFromWindow(hwnd, MonitorDefaultToNearest);
            GetMonitorInfo(monitorContainingApplication, monitorInfo);
            RECT rcWorkArea = monitorInfo.rcWork;
            //check for a framechange - but ignore initial draw. x,y is top left of current monitor so must be a maximise
            if (((winPos.flags & SWP_FRAMECHANGED) == SWP_FRAMECHANGED) && (winPos.flags & SWP_NOSIZE) != SWP_NOSIZE && winPos.x == rcWorkArea.left && winPos.y == rcWorkArea.top)
            {
                //set max size to the size of the *current* monitor
                var width = Math.Abs(rcWorkArea.right - rcWorkArea.left);
                var height = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
                winPos.cx = width;
                winPos.cy = height;
                Marshal.StructureToPtr(winPos, lParam, true);
                handled = true;
            }                       
            break;
    }
    return (IntPtr)0;
}


//all the helpers for dealing with this COM crap
[DllImport("user32")]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

[DllImport("user32")]
internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

private const int MonitorDefaultToNearest = 0x00000002;

[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
    public IntPtr hwnd;
    public IntPtr hwndInsertAfter;
    public int x;
    public int y;
    public int cx;
    public int cy;
    public int flags;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFO
{
    public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
    public RECT rcMonitor;
    public RECT rcWork;
    public int dwFlags;
}

[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

1
仅因你的评论“所有用于处理这个COM垃圾的助手”,我点了赞。 - Nick Fisher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接