关于SetWindowLong配合DWL_MSGRESULT处理NM_CUSTOMDRAW消息的疑问
Win32 对话框自定义绘制相关问题解答
1. 为什么既要调用SetWindowLong设置DWL_MSGRESULT,又要返回TRUE?
这是Win32**对话框过程(DlgProc)**和普通窗口过程(WndProc)的核心规则差异导致的:
- 普通窗口过程的返回值是
LRESULT类型,直接作为当前消息的处理结果返回给消息发送方 - 对话框过程的返回值是
BOOL类型,仅用来告诉系统「当前消息我是否已经处理」:- 返回
TRUE表示你已经处理完这个消息,系统会从窗口的DWL_MSGRESULT属性中读取你预先存入的值,作为该消息的最终处理结果返回给发送方 - 返回
FALSE表示你没有处理该消息,系统会走默认对话框逻辑处理
你代码里的两个操作完全是各司其职,不是返回两个值:
- 返回
SetWindowLong(hWnd, DWL_MSGRESULT, (LONG)CustomDraw(lParam))是把CustomDraw返回的绘制控制码(比如CDRF_NOTIFYITEMDRAW)存入指定位置,供系统读取作为NM_CUSTOMDRAW消息的业务返回值- 后续返回
TRUE是告诉对话框管理器:这个消息我处理完了,你去DWL_MSGRESULT里拿结果就行
2. DialogBox()的返回值存储逻辑
DialogBox()是模态对话框的创建接口,它的返回值和DWL_MSGRESULT没有关系:
- 模态对话框运行期间会阻塞调用线程,直到你调用
EndDialog(nResult)关闭对话框,你传入EndDialog的nResult参数就是DialogBox()最终的返回值 - 这个值存储在系统维护的对话框内部管理结构中,不会被忽略,调用方可以通过这个返回值判断用户的操作(比如点击了确定还是取消、选择了哪条数据等)
3. 为什么CustomDraw会被多次调用?
多次调用和SetWindowLong没有关系,完全是ListView的自定义绘制机制和你写的控制逻辑决定的:NM_CUSTOMDRAW是ListView主动发送给父窗口的通知消息,你在CustomDraw里返回的控制码会告诉ListView后续是否需要继续发送通知:
- 第一次触发是绘制周期开始,
dwDrawStage = CDDS_PREPAINT,你返回CDRF_NOTIFYITEMDRAW,要求ListView每个条目绘制前都再发一次NM_CUSTOMDRAW - 接下来每个条目绘制前触发,
dwDrawStage = CDDS_ITEMPREPAINT,你返回CDRF_NOTIFYSUBITEMDRAW,要求ListView当前条目的每个子项绘制前都再发一次NM_CUSTOMDRAW - 之后每个子项绘制前都会触发一次,
dwDrawStage = CDDS_SUBITEM | CDDS_ITEMPREPAINT,你返回CDRF_NEWFONT告诉系统你已经修改了绘制颜色,用你设置的参数绘制
假设你的ListView有10行、6列,一次完整重绘就会触发1 + 10 + 10*6 = 71次CustomDraw调用,属于正常逻辑。
4. 为什么直接返回CustomDraw的执行结果不生效?
还是源于对话框过程的规则限制:
你直接返回CustomDraw的结果(比如CDRF_NOTIFYITEMDRAW),虽然这个值是非0的,会被系统识别为TRUE,但你没有把这个控制码存入DWL_MSGRESULT,系统读到的DWL_MSGRESULT是未初始化的脏值,用错误的控制码处理绘制流程,自然不会按你的预期执行自定义绘制逻辑。
而SetWindowLong的返回值是修改前DWL_MSGRESULT的旧值,和CustomDraw的结果没有任何关系,返回它也没有意义。
如果你是在普通窗口的窗口过程里处理NM_CUSTOMDRAW,直接返回CustomDraw的结果是可以生效的,只有对话框过程需要遵守「设置DWL_MSGRESULT + 返回TRUE」的规则。
内容的提问来源于stack exchange,提问作者Awynon X
相关产品推荐
相关产品推荐

