如何通过子类化在窗体最大化操作发生前检测并处理?
问题排查与解决方案
一、结构体数据异常的核心原因
你用CopyMemory时踩了值类型传值的坑:WINDOWPOS是值类型,直接把它传给CopyMemory,方法只会修改传入的副本,根本不会更新你定义的wPos变量,所以你读到的都是默认初始化的空数据,自然异常。
而且.NET本身就有更安全的结构体转换方法,完全没必要手动调用CopyMemory。
二、修正后的代码实现
1. 精简API与结构体定义
删掉原来的APIs类,用.NET自带的Marshal类处理结构体:
public class APIConstants { public const uint SWP_NOSIZE = 0x0001; public const uint SWP_FRAMECHANGED = 0x0020; // 窗体样式变更时会带这个标志 public const int WM_WINDOWPOSCHANGING = 0x0046; } public class Structures { [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 uint flags; } }
2. 修正WndProc方法
protected override void WndProc(ref Message m) { if (m.Msg == APIConstants.WM_WINDOWPOSCHANGING) { // 直接把LParam转成WINDOWPOS结构体,一步到位 Structures.WINDOWPOS wPos = Marshal.PtrToStructure<Structures.WINDOWPOS>(m.LParam); // 判断是否是最大化操作:看cx/cy是否等于屏幕工作区尺寸,再结合SWP_FRAMECHANGED标志 var screenWorkArea = Screen.FromHandle(this.Handle).WorkingArea; bool isMaximizing = (wPos.cx == screenWorkArea.Width && wPos.cy == screenWorkArea.Height) && (wPos.flags & APIConstants.SWP_FRAMECHANGED) != 0; if (isMaximizing) { // 替换成你的全局变量判断逻辑 if (GlobalSettings.DisableMaximize) { // 修改结构体,阻止最大化:恢复原窗体尺寸,加上SWP_NOSIZE标志 wPos.cx = this.Width; wPos.cy = this.Height; wPos.flags |= APIConstants.SWP_NOSIZE; // 把修改后的结构体写回LParam Marshal.StructureToPtr(wPos, m.LParam, false); Debug.WriteLine("已阻止窗体最大化"); // 如果要彻底取消这个消息,直接return就行,不用调用base.WndProc // return; } } // 现在能正确检测SWP_NOSIZE标志了 if ((wPos.flags & APIConstants.SWP_NOSIZE) != 0) { Debug.WriteLine("检测到SWP_NOSIZE标志"); } } // 必须调用基类方法,不然窗体移动、调整尺寸这些基础功能会挂掉 base.WndProc(ref m); }
三、关于FormBorderStyle=FixedSingle的影响
当窗体设为FixedSingle时,默认MaximizeBox是false,用户点不了最大化按钮,但如果你代码里手动开了MaximizeBox=true,或者用API强制触发最大化,还是会触发WM_WINDOWPOSCHANGING消息,所以上面的代码依然能用。
四、关键提醒
- 处理完消息一定要调用
base.WndProc(ref m),不然窗体基本交互会失效; - 如果要完全取消某个消息,处理完直接
return,别调用基类; - 用
Marshal.PtrToStructure和Marshal.StructureToPtr是.NET处理Win32结构体的标准操作,比手动调用CopyMemory靠谱多了,不容易出内存错误。
内容的提问来源于stack exchange,提问作者Arvo Bowen
相关产品推荐
相关产品推荐

