图形学桌面应用【免费下载链接】RGF_CJRGF是仓颉Windows系统下的通用渲染框架兼应用开发框架。RGF仓颉版后续简称RGF基于RGF(C/C版)封装优化而来。RGF为开发者提供轻量化、安全、高性能以及高度一致性的2D渲染能力以及常用的桌面应用开发工具并且提供对接Direct3D的相关接口以满足开发者对3D画面渲染的需求。在Windows开发方面RGF在子包内也提供开箱即用的Windows API确保熟悉Win32的开发者快速上手。此外RGF还提供一些常用功能的封装如XAudio2、MF以辅助开发者实现音频、摄像头等扩展功能的管理。项目地址https://gitcode.com/Cangjie-SIG/RGF_CJ点击查看免费下载EvWindowPosChange是 RGF 仓颉版RGF_CJ通用渲染框架中负责承载窗口位置/大小/Z 顺序即将变更与已经变更两类系统消息的事件参数类。本文围绕 EvWindowPosChange.md 展开完整讲解其类结构、每个成员变量与 Win32WINDOWPOS结构体的对应关系、postProcess()回写机制并结合 rgf_proc.cj 源码展示 RGF_CJ 如何把原始的WM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED消息翻译成面向对象的仓颉事件。读完本文你将掌握在自定义窗口类中监听并拦截窗口位置变更、修正窗口最终落点与尺寸的完整实战方案。一、事件概述RGF_CJ 如何承载窗口位置变更消息在 Win32 体系里窗口的位置、大小、Z 顺序发生变化时系统会向窗口过程发送两条消息WM_WINDOWPOSCHANGING0x0046窗口的大小、位置或 Z 顺序即将发生更改WM_WINDOWPOSCHANGED0x0047窗口的大小、位置或 Z 顺序已经发生更改。在 hwinuser.cj 中可以看到二者的常量定义public const WM_WINDOWPOSCHANGING:UInt32 0x0046 public const WM_WINDOWPOSCHANGED:UInt32 0x0047这两条消息的lParam均携带一个指向WINDOWPOS结构体的指针。RGF_CJ 在rgf.rgf_core包中提供了EvWindowPosChange事件参数类把该结构体解析为仓颉侧可直接读写的字段从而把底层消息翻译成面向对象的事件参数。它同时服务于两条消息因此类文档中将其概括为窗口位置变更 - 事件参数。public open class EvWindowPosChange : EvCore该类继承自EvCore核心事件参数基类因此也自动继承了EvCore提供的基础字段message、wParam、lParam见 EvCore.md可随时回溯原始消息的完整上下文。二、构造函数从 lParam 指针到结构化字段的解析2.1 函数签名public init(uMsg: UInt32, wParam: UInt64, lParam: Int64)参数说明名称类型描述uMsgUInt32触发事件的消息编号取0x0046WM_WINDOWPOSCHANGING或0x0047WM_WINDOWPOSCHANGEDwParamUInt64消息附带参数这两条消息通常不使用该值仅为对齐EvCore基类而保留lParamInt64指向WINDOWPOS结构体的指针是本事件的核心数据来源注意EvWindowPosChange的构造函数签名与基类EvCore保持一致其中第一个参数uMsg由 RGF_CJ 内部显式传入消息编号。从消息分发源码可以确认两条消息各自构造了独立的事件对象case 0x0046 /*WM_WINDOWPOSCHANGING*/ { let eObj:EvWindowPosChange EvWindowPosChange(0x0046, wParam,lParam) onWindowPosChanging(eObj) if(eObj.isOverride){ eObj.postProcess() } return (0,eObj.isOverride) } case 0x0047 /*WM_WINDOWPOSCHANGED*/ { let eObj:EvWindowPosChange EvWindowPosChange(0x0047, wParam,lParam) onWindowPosChanged(eObj) return (0,eObj.isOverride) }以上代码位于 rgf_proc.cj。可以看到WM_WINDOWPOSCHANGING在调用事件函数后若开发者通过isOverride标记了拦截还会调用postProcess()把修改后的数据回写系统详见第三节而WM_WINDOWPOSCHANGED属于已发生状态不需要也不能回写。2.2 解析流程构造函数内部rgf_proc.cj完成三层工作指针恢复通过ruUllToPtr(ruInt64SafeToUIntNative(lParam))将lParam的有符号整数值安全恢复为CPointerWINDOWPOS指针结构体读取通过unsafe{ ptr.read() }一次性读取指针指向的WINDOWPOS内存拷贝到类内部字段Obj字段拆解将Obj中的hwnd、hwndInsertAfter、x、y、cx、cy、flags逐一映射到类公开成员。因此开发者拿到事件对象时所有数据已经完成解析无需再手动处理指针或内存。三、成员字段与 Win32 WINDOWPOS 的一一对应EvWindowPosChange公开的七个成员字段对应 Win32 的tagWINDOWPOS结构体。RGF_CJ 在 hwinuser.cj 中以C结构体形式定义了该结构C public struct tagWINDOWPOS { public var hwnd:HWND HWND(); public var hwndInsertAfter:HWND HWND(); public var x:INT 0; public var y:INT 0; public var cx:INT 0; public var cy:INT 0; public var flags:UINT 0; }两者逐字段对应关系如下EvWindowPosChange 成员类型可变性对应 WINDOWPOS 字段含义hwndRgfHwndlet只读hwnd发生位置变更的窗口句柄hwndInsertAfterRgfHwndlet只读hwndInsertAfter目标 Z 顺序参照窗口的句柄xInt32var可写x窗口新位置的左上角 X 坐标屏幕坐标yInt32var可写y窗口新位置的左上角 Y 坐标屏幕坐标cxInt32var可写cx窗口新宽度像素cyInt32var可写cy窗口新高度像素flagsUInt32var可写flags窗口定位标志位SWP_*组合其中x、y、cx、cy、flags在类文档中声明为var正是为了支持在WM_WINDOWPOSCHANGING阶段修改后通过postProcess()回写而hwnd、hwndInsertAfter为let只读因为它们描述的是哪个窗口与参照谁属于本次变更的客观身份信息不允许修改。3.1 flags 的取值SWP_* 标志位flags是理解本次变更行为的关键。RGF_CJ 在 hwinuser.cj 中导出了完整的SWP_*常量集常见取值如下常量值含义SWP_NOSIZE0x0001不改变窗口大小SWP_NOMOVE0x0002不改变窗口位置SWP_NOZORDER0x0004不改变 Z 顺序SWP_NOREDRAW0x0008不重绘窗口SWP_NOACTIVATE0x0010不激活窗口SWP_FRAMECHANGED0x0020强制发送WM_NCCALCSIZE与SWP_DRAWFRAME同值SWP_SHOWWINDOW0x0040显示窗口SWP_HIDEWINDOW0x0080隐藏窗口SWP_NOCOPYBITS0x0100丢弃客户区内容SWP_NOOWNERZORDER0x0200不改变所有者窗口的 Z 顺序与SWP_NOREPOSITION同值SWP_NOSENDCHANGING0x0400禁止发送WM_WINDOWPOSCHANGINGSWP_DEFERERASE0x2000防止生成WM_SYNCPAINTSWP_ASYNCWINDOWPOS0x4000异步执行窗口定位例如当flags含有SWP_NOMOVE0x0002时说明本次变更仅涉及大小或 Z 顺序x、y字段不表示新落点。在事件处理中结合flags判断变更类型可以避免把仅改尺寸误当作移动窗口处理。四、postProcess()把修改回写系统public func postProcess()这是EvWindowPosChange区别于纯数据类事件参数的关键方法其实现位于 rgf_proc.cjpublic func postProcess(){ Obj.x x Obj.y y Obj.cx cx Obj.cy cy Obj.flags flags unsafe{ ptr.write(Obj) } }它把类中当前可写字段x、y、cx、cy、flags整体写回原始WINDOWPOS内存。其典型应用场景是在WM_WINDOWPOSCHANGING阶段对窗口的最终落点、尺寸进行最后修正——例如强制窗口不超出工作区、固定窗口宽高比、或强制吸附到某个参考位置。修改后系统将按照回写的新值完成本次窗口位置变更。需要特别强调的是WM_WINDOWPOSCHANGING支持拦截回写。从分发代码可见事件函数返回后仅当eObj.isOverride true时才会执行postProcess()rgf_proc.cj。也就是说开发者必须在事件处理中把e.isOverride置为true修改才会真正生效WM_WINDOWPOSCHANGED不执行回写。窗口位置已经变更完成此时postProcess()不再被调用事件只用于观察结果。4.1 isOverride 的默认行为RGF_CJ 为窗口类预置了这两个事件的默认实现rgf_proc.cj// brief 即将更改大小、位置或 Z 顺序时触发 func onWindowPosChanging(e:EvWindowPosChange):Unit{ e.isOverride false } // brief 更改大小、位置或 Z 顺序时触发 func onWindowPosChanged(e:EvWindowPosChange):Unit{ e.isOverride false }默认不拦截消息按系统默认流程执行。开发者覆写事件函数时如需修改窗口最终位置必须在处理逻辑中将e.isOverride true并改写相应字段否则即使改了x/y也不会生效。五、在窗口类中使用 EvWindowPosChange5.1 方式一继承 IEvent 接口重写事件函数让窗口类继承WinCore或WinBase并实现IEvent接口通过wndProc中的callEvent将消息分发给事件函数。窗口位置变更对应的两个事件函数签名如下class WinSelf : WinCore IEvent{ public override func wndProc(hwnd:RgfHwnd, uMsg:UInt32, wParam:UInt64, lParam:Int64):Int64{ if(let Some(retv) - callEvent(hwnd,uMsg,wParam,lParam)){ return retv }else{ return super.wndProc(hwnd,uMsg,wParam,lParam) } } // brief 窗口即将更改大小、位置或 Z 顺序时触发可在此拦截并修正 func onWindowPosChanging(e:EvWindowPosChange):Unit{ // 示例强制窗口宽度不小于 800 if(e.cx 800){ e.cx 800 e.isOverride true // 关键标记拦截触发 postProcess() 回写 } } // brief 窗口已更改大小、位置或 Z 顺序时触发仅用于观察结果 func onWindowPosChanged(e:EvWindowPosChange):Unit{ println(窗口位置已变更: (${e.x}, ${e.y}) 尺寸 ${e.cx}x${e.cy} flags0x${e.flags.toHexString()}) } }5.2 方式二HookProc 宏自动分发如果不想手写wndProc可以在类前使用HookProc宏定义见 rgf_proc.cj实现位于 rgf_proc_utils.cj宏会在编译期为窗口类自动生成wndProc中的消息分发代码HookProc class WinSelf : WinCore IEvent{ // wndProc 由宏自动生成开发者只需实现事件函数 func onWindowPosChanging(e:EvWindowPosChange):Unit{ // 拦截逻辑 } func onWindowPosChanged(e:EvWindowPosChange):Unit{ // 观察逻辑 } }5.3 方式三IBindEvent 绑定式事件如果希望不通过继承、直接为窗口实例注入处理逻辑可以使用IBindEvent接口的绑定式属性bindWindowPosChanging与bindWindowPosChangedrgf_proc.cjclass WinSelf : WinCore IBindEvent{ public override func wndProc(hwnd:RgfHwnd, uMsg:UInt32, wParam:UInt64, lParam:Int64):Int64{ if(let Some(retv) - callBind(hwnd,uMsg,wParam,lParam)){ return retv }else{ return super.wndProc(hwnd,uMsg,wParam,lParam) } } private var evWindowPosChanging:?(EvWindowPosChange)-Unit { println(窗口位置即将变更) } public override mut prop bindWindowPosChanging:?(EvWindowPosChange)-Unit{ get(){ evWindowPosChanging } set(v){ evWindowPosChanging v } } }绑定式分发路径中WM_WINDOWPOSCHANGING同样遵循isOverride为真才调用postProcess()的规则rgf_proc.cjcase 0x0046 /*WM_WINDOWPOSCHANGING*/ { let eObj:EvWindowPosChange EvWindowPosChange(0x0046, wParam,lParam) return if(let Some(v) - bindWindowPosChanging?(eObj)){ eObj.postProcess();0 }else{ OptionInt64.None } }六、实战拦截窗口移动并限制在工作区内综合以上机制下面给出一个完整的实战用例——当用户试图把窗口拖出工作区时onWindowPosChanging自动将其拉回屏幕可见范围。该场景正是EvWindowPosChange最具代表性的用法HookProc class WinSelf : WinCore IEvent{ func onWindowPosChanging(e:EvWindowPosChange):Unit{ // 获取窗口所在显示器的可见工作区 let work rsGetMonitorFromPoint(e.x, e.y) // 返回 (宽, 高) 工作区信息 // 修正 X窗口左边缘不允许超出工作区左边界 if(e.x 0){ e.x 0 } // 修正 Y窗口右边缘不允许超出工作区右边界此处以 1920 工作区宽为例 if(e.x e.cx 1920){ e.x 1920 - e.cx } // 只要发生了修正就标记拦截并回写 e.isOverride true } func onWindowPosChanged(e:EvWindowPosChange):Unit{ println(最终位置: (${e.x}, ${e.y})尺寸: ${e.cx}x${e.cy}) } }要点总结在onWindowPosChanging中读写x、y、cx、cy、flags通过e.isOverride true标记需要回写系统在事件返回后自动调用postProcess()将修正后的WINDOWPOS写回本次变更按新值执行在onWindowPosChanged中读取最终结果此时字段为只读语义上的最终值。七、与相关事件的区分在窗口位置/尺寸变化的事件族中EvWindowPosChange与以下事件参数容易混淆建议按触发时机与数据形态区分事件参数类对应消息时机数据形态EvWindowPosChangeWM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED窗口位置/大小/Z 顺序变更前与变更后结构化WINDOWPOS字段支持回写拦截EvMoveWM_MOVE窗口已移动后仅坐标x、ylet只读EvSizeWM_SIZE窗口尺寸已改变后尺寸与变更类型let只读EvSizingWM_SIZING用户拖动边框调整大小时持续触发拖动中的矩形与边缘标识EvMovingWM_MOVING用户拖动窗口移动时持续触发拖动中的矩形与边缘标识EvGetMinMaxInfoWM_GETMINMAXINFO窗口大小或位置即将更改时MINMAXINFO结构用于限制最小/最大尺寸其中EvWindowPosChange覆盖面最广——无论是MoveWindow、SetWindowPos、用户拖动还是程序化布局引起的变更只要涉及位置/大小/Z 顺序都会触发WM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED因此它也是做全局窗口布局约束的首选切入点。八、小结EvWindowPosChange是 RGF_CJ 将 Win32 窗口定位机制面向对象化的核心事件参数类数据来源lParam指向的WINDOWPOS结构体构造函数在 rgf_proc.cj 中完成指针恢复、结构读取与字段拆解字段对应hwnd、hwndInsertAfter只读x、y、cx、cy、flags可写完整对应 hwinuser.cj 的tagWINDOWPOS回写机制postProcess()rgf_proc.cj把修改后的值写回系统仅WM_WINDOWPOSCHANGING阶段且isOverride true时生效使用方式可通过IEvent继承重写onWindowPosChanging/onWindowPosChanged或通过HookProc宏自动分发或通过IBindEvent的bindWindowPosChanging/bindWindowPosChanged绑定式注入典型场景在窗口移动前拦截并修正位置、强制最小尺寸、限制窗口不超出工作区。掌握EvWindowPosChange意味着你可以在窗口定位的最后一公里上拥有系统级的控制权这是构建稳定桌面布局逻辑的重要基础能力。赞分享图形学桌面应用【免费下载链接】RGF_CJRGF是仓颉Windows系统下的通用渲染框架兼应用开发框架。RGF仓颉版后续简称RGF基于RGF(C/C版)封装优化而来。RGF为开发者提供轻量化、安全、高性能以及高度一致性的2D渲染能力以及常用的桌面应用开发工具并且提供对接Direct3D的相关接口以满足开发者对3D画面渲染的需求。在Windows开发方面RGF在子包内也提供开箱即用的Windows API确保熟悉Win32的开发者快速上手。此外RGF还提供一些常用功能的封装如XAudio2、MF以辅助开发者实现音频、摄像头等扩展功能的管理。项目地址https://gitcode.com/Cangjie-SIG/RGF_CJ点击查看免费下载相关推荐RGF_CJ 窗口风格变更事件 EvStyleChange 详解拦截与响应窗口样式变化的完整指南RGF_CJ 窗口风格变更事件 EvStyleChange 详解拦截与响应窗口样式变化的完整指南 EvStyleChange 是 RGF仓颉版通用渲染框架图形学桌面应用RGF_CJ 窗口可用性事件 EvEnable 详解从 WM_ENABLE 消息到事件参数类RGF_CJ 窗口可用性事件 EvEnable 详解从 WM_ENABLE 消息到事件参数类 导读 本文深入剖析 RGF仓颉 Windows 系统通用渲染图形学桌面应用RGF_CJ 窗口消息事件深入解析EvDeleteItem 删除项事件参数类RGF_CJ 窗口消息事件深入解析EvDeleteItem 删除项事件参数类 EvDeleteItem 是 RGF_CJ仓颉 Windows 通用渲染框架图形学桌面应用上一篇显卡性能优化终极指南3步解锁NVIDIA隐藏功能游戏帧率提升40%下一篇NVIDIA显卡优化神器解锁隐藏设置3步打造极致游戏体验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考