7

In my C# .NET 4 application, I use WndProc to process some messages mostly dealing with resizing the application to and from full screen.

Right now I am just handling SC_MAXIMIZE and WM_NCLBUTTONDBLCLK to determine if the window is being resized to or from a maximized state (I know I don't need WndProc to handle SC_MAXIMIZE, but Form_Resize didn't seem to fire for a WM_NCLBUTTONDBLCLK message when I double-click on the application's title bar.

Now I noticed that if I Aero Snap the window to the top of the screen to maximize it, neither of the above messages are posted so certain logic is not applied when the window is maximized via Aero Snap. I only want to handle the message if the window is snapped to the top of the screen rather than the right or left, or if a window is unsnappped from maximized position.

I couldn't find any of the window messages related to Aero Snap. Does anyone know of any references for those messages?

drew010
  • 68,777
  • 11
  • 134
  • 162

2 Answers2

6

I'm guessing there aren't any special messages here; Aero is likely just using the plain Win32 APIs - ShowWindow(SW_MAXIMIZE) and similar.

The thing to uderstand with the SC_ messages are that those are requests from a menu asking the window to resize/restore/etc itself, but that is not the only mechanism for changing the windows's size. What's probably happening is that when a window gets SC_MAXIMIZE, the DefWndProc implements this by calling ShowWindow(SW_MAXIMIZE).

Your best best is to listen to the WM_SIZE message, which the window receives, regardless of what triggered the size change: system menu, API, or other means. In particular, the lParam will let you know if the window was maximized (SIZE_MAXIMIZED) or restored (SIZE_RESTORED).

BrendanMcK
  • 14,252
  • 45
  • 54
  • 3
    This is correct. There are no special notifications sent as a result of Aero Snap. It uses the standard `WM_MOVING`/`WM_MOVE` and `WM_SIZING`/`WM_SIZE` messages. If you process these without calling `DefWindowProc`, Aero Snap will not work for your window. Yes, you could listen for `WM_SIZE`, but you're probably better off in general using [`WM_WINDOWPOSCHANGED`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms632652.aspx). It's a "new" function, introduced in, err, Windows 3.1. :-) [Relevant reading](http://blogs.msdn.com/b/oldnewthing/archive/2008/01/15/7113860.aspx). – Cody Gray - on strike Feb 17 '12 at 02:35
  • 1
    The catch with POSCHANGED is you get it for *any* move/size change at all, including when the window is being moved around prior to the "snap", so you need to do more filtering. And I don't know if you can determine a maximize 'snap' from its parameters - there's no obvious "window has been maximized" indicator. With WM_SIZE, check lParam and you're done! – BrendanMcK Feb 17 '12 at 02:52
  • Yes, you need to do some filtering. That's basically inevitable, you'll have to filter `WM_SIZE` to make sure that you only deal with resize events initiated by Aero Snap. The point is that all of your handling code is in one place. I don't really see the difference between a `switch` statement inside of your `WM_WINDOWPOSCHANGED` message handler, and a `switch` statement inside of your window procedure that handles `WM_MOVE`, `WM_SIZE`, etc. I suppose with great power comes great responsibility; either one will work. – Cody Gray - on strike Feb 17 '12 at 02:54
  • Can you suggest what filtering might work with WM_WINDOWPOSCHANGED then? It seems to work at a lower-level, so you know the exact window position and whether there's a frame or not, but the higher-level semantics of "maximized" don't seem to be present. So if you want to specifically check for maximize/restore events, seems WM_SIZE is the best event. (I did look up WM_WINDOWPOSCHANGED before answering, and figured it wasn't the right tool for this specific problem; WM_SIZE just seems a much better/simpler fit here.) – BrendanMcK Feb 17 '12 at 02:59
  • (Also, my reading of the Qu is not that they want to detect snap maximize only/specifically; more that they are already getting other maximized events via SC_MAXIMIZE, but missing out on aero snap; so want to get *all*, both menu, double-click-titlebar, and also aero.) – BrendanMcK Feb 17 '12 at 03:01
  • Yes, that's precisely why I suggested using the message that I did, because they're interested in handling lots of different events. It's not worth arguing about though because it doesn't really matter; either one will work fine. – Cody Gray - on strike Feb 17 '12 at 03:04
  • So *how* do you suggest WM_WINDOWPOSCHANGED can be used to detect maximize/restores then, as per the qu? – BrendanMcK Feb 17 '12 at 03:20
  • Do it the same way that `DefWindowProc` does it: look at the values in the `WINDOWPOS` structure. I'd probably just call `GetWindowPlacement` though. – Cody Gray - on strike Feb 17 '12 at 03:29
  • 2
    This answer is incorrect, except in the small part that WM_SIZE can be used to detect the new size. "Snapped" and "maximized" aren't the same thing: IsZoomed(hwnd) returns false. The window's system menu shows that Maximize and Minimize are both enabled, but Restore is not, because the window is in the normal/restored state. WM_SIZE is received with SIZE_RESTORED, not SIZE_MAXIMIZED. – Lexikos Jun 04 '20 at 21:36
3

Here is the code for handling WM_WINDOWPOSCHANGING message for Maximise instead of WM_SIZE message. Thanks to the 20 or more questions on SO that I had to read to find all the bits to put it together and get it working. This addresses the problems that I was having with multiple monitors using different resolutions.

//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;
}
JumpingJezza
  • 5,498
  • 11
  • 67
  • 106