例如,考虑SendInput
。签名如下所示:
UINT WINAPI SendInput(
_In_ UINT nInputs,
_In_ LPINPUT pInputs,
_In_ int cbSize
);
文档说:
cbSize [in]类型:int INPUT结构的大小(以字节为单位)。如果 cbSize不是INPUT结构的大小,函数失败。
由于该函数已经使用了INPUT结构(并且可能对其各个字段做了一些事情),所以它不应该预先知道结构的大小吗?
我可以想象的唯一原因是,这是一种奇怪的向后兼容性技巧,可以使较旧的库二进制文件与可能在结构末尾引入新字段的较新头文件兼容。
答案 0 :(得分:14)
这是结构版本化的一种简单形式。
API的更高版本可以在结构的末尾添加更多字段,这将改变其大小。为旧版本编写的程序不会在较新的字段中设置值,而cbSize参数将反映出这一点。该API可以检查cbSize并知道它真正具有哪种版本的结构,并在必要时为新字段提供默认值。
另一种方法是定义一个与旧结构有很多共同点的新结构,然后创建一个与旧结构非常相似的新API。这有很多代码重复,并且使旧的程序更难以使用较新的SDK重新编译并继续工作。
使用大小字段消除了对大量重复代码的需求。这是用C做事的常用方法,但它的类型安全性较低。
但它也有点危险。如果调用者没有正确设置大小字段,或者API实现不是非常小心,则此方案可能会导致访问冲突,读取未初始化的字段或写入结构的末尾。< / p>
答案 1 :(得分:6)
是的,这样做是为了让Microsoft可以在未来版本的winapi中更改INPUT结构。从传递的cbSize中,它可以判断程序是使用旧版本还是新版本的结构。从结构本身来看,这是不容易发现的。
将它作为函数的参数传递并不常见,它们通常将cbSize
嵌入作为结构的成员。例如,比较W3C specification for HTML5,MENUITEMINFO,SCROLLINFO,MSGBOXPARAMS。使用SendInput()和GetMouseMovePointsEx()等函数的单个参数更容易,因为它们采用结构数组。
对WNDCLASSEX,LVCOLUMN等结构采取另一种方法,他们根本不使用cbSize。使用他们的api需要在LVITEM中明确指定所需的版本。