你可能会经常看到这样的通知消息:XXN_GETDISPINFO,是不是有点印象了?
这个消息的使用场景:当控件要求其父窗口生成(标记为延迟呈现的)信息时,将使用这个 XXN_GETDISPINFO 通知,这些信息可以通过 LPSTR_TEXTCALLBACK 等来显式生成,也可以通过作为所有者数据控件隐式生成。是不是有点绕?
事实上,控件实际上只是请求有关条目信息的代码(通过类似 LVM_GETITEM 的消息)和生成该信息的代码(LVN_GETDISPINFO 处理程序)之间的中间人。
换句话说,代码执行流程,如下所述:
首先,一个想从列表视图控件中获取条目信息的人会创建一个 LVITEM 结构,并根据需要根据掩码初始化 LVITEM.mask 和其他字段。
(例如,如果设置了 LVIF_TEXT 标志,则还必须将 LVITEM.pszText 和 LVITEM.cchTextMax 设置为缓冲区及其大小。)
然后,它向列表视图控件发送一条LVM_GETITEM消息。列表视图控件查看 LVITEM.mask 以查看需要填写的信息。列表视图可以自行提供的一些信息。信息的其他部分需要列表视图控件的父窗口的帮助。
例如,如果 LVITEM.mask 设置了 LVIF_TEXT 标志,并且项的文本设置为 LPSTR_TEXTCALLBACK,则列表视图需要查阅其父窗口以获取文本。
列表视图控件将 LVN_GETDISPINFO 消息发送给其父窗口,说:“嘿,有人在找信息,请提供 LVITEM.mask 成员中要求的信息。” 父窗口处理消息后,结果将返回给原始调用方。
下面是在返回结果之前发生的一个小奖励步骤: 如果父窗口在 LVITEM.mask 中设置了 LVIF_DI_SETITEM 标志,则返回的值也会保存到列表视图控件中,就像您发送了LVM_SETITEM消息一样。例如,如果设置 LVIF_DI_SETITEM 标志以响应LVIF_TEXT 请求,则返回的文本将保存到列表视图项中,覆盖以前的值 LPSTR_TEXTCALLBACK。如果您只想计算一次结果并让列表视图从一开始就缓存结果,这将非常方便。
请注意,在整个过程中,LVITEM.mask 控制列表视图的原始调用方请求的信息,以及其父视图的列表视图请求的信息。
如果您错误地更改了 LVITEM.mask 的值(除了设置 LVIF_DI_SETITEM 标志,如“奖励步骤”中所述),那么您就干扰了这个 “推卸责任” 的游戏。父窗口处理消息后,结果将返回给原始调用方。但是,如果您修改了 LVITEM.mask,则返回给调用方的结果与调用方请求的结果不同!
例如,如果列表视图看到设置了 LVIF_TEXT 标志,则会将父窗口提供的字符串复制回调用方的缓冲区。但是等一下,如果父窗口是设置 LVIF_TEXT 标志的人,这意味着原始呼叫者没有要求文本。没有缓冲区可以将结果复制回其中。列表视图将字符串复制到未初始化的指针,并因此发生各种内存损坏。
在处理 XXN_GETDISPINFO 通知时,请遵守惯例。掩码指定了要求您提供哪些信息(因此哪些信息将被复制回原始呼叫者)。如果您更改此掩码,原始呼叫者将大吃一惊。
这就像在餐厅里当厨师修改顾客的订单一样。
“哦,这位顾客不想要香菜,那我给他加点葱花作为替代吧。”
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《The GETDISPINFO notifications tell you what information they want》