为什么有些WinAPI函数需要传递的结构大小作为参数?

nic*_*yte 4 c windows winapi

例如,考虑一下SendInput.签名如下所示:

UINT WINAPI SendInput(
  _In_ UINT    nInputs,
  _In_ LPINPUT pInputs,
  _In_ int     cbSize
);
Run Code Online (Sandbox Code Playgroud)

文件说:

cbSize [in]类型:int INPUT结构的大小(以字节为单位).如果cbSize不是INPUT结构的大小,则该函数将失败.

由于该函数已经使用了INPUT结构(并且可能对其各种字段做了一些事情),所以它不应该预先知道结构的大小吗?

我可以想象的唯一原因是,这是一种奇怪的向后兼容性技巧,可以使较旧的库二进制文件与可能在结构末尾引入新字段的较新头文件兼容.

Adr*_*thy 15

这是结构版本化的简单形式.

API的更高版本可以在结构的末尾添加更多字段,这将改变其大小.为旧版本编写的程序在较新的字段中没有设置值,cbSize参数将反映该值.该API可以检查cbSize并知道它真正具有哪种版本的结构,并在必要时为新字段提供默认值.

另一种方法是定义一个与旧结构有很多共同点的新结构,然后创建一个与旧结构非常相似的新API.这是很多代码重复,并且使旧的程序更难以使用较新的SDK重新编译并继续工作.

使用大小字段消除了对大量重复代码的需要.这是在C中执行操作的常用方法,但它的类型安全性较低.

但它也有点危险.如果调用者未正确设置size字段或者API实现不是非常小心,则此方案可能会导致访问冲突,读取未初始化的字段或写入结构的末尾.


Han*_*ant 6

是的,这样做是为了让Microsoft可以在未来版本的winapi中更改INPUT结构.从传递的cbSize中,它可以判断程序是使用旧版本还是新版本的结构.从结构本身来看,这是不容易发现的.

将它作为函数的参数传递并不常见,它们通常cbSize作为结构的一部分嵌入.比较WNDCLASSEX,MENUITEMINFO,SCROLLINFO,MSGBOXPARAMS等.使用SendInput()和GetMouseMovePointsEx()等函数的单个参数更容易,因为它们采用结构数组.

另一种方法是采用LVITEM,LVCOLUMN等结构,它们根本不使用cbSize.使用它们的api需要在清单中明确指定所需的版本.

  • 它是一个单独的参数,因为你传递的是一个结构数组,并且初始化每个结构的size成员会很烦人(如果大小不同意怎么办?) (4认同)