我注意到在c/c++中,很多Win32 API结构需要告诉它们的大小。
例如:
编辑:哦,是的,我是指“cbFormat”。
someStruct.cbFormat = sizeof(SomeStruct)
为什么会这样?这只是因为历史原因吗?还有,你知道"pb"代表什么吗?编辑:哦,是的,我是指“cbFormat”。
这是为了在Windows API扩展时保持向后兼容性。
想象以下声明:
struct WinData
{
long flags;
}
BOOL GetWinData(WinData * wd);
你正在这样调用它:WinData wd;
GetWinData(&wd);
未来的操作系统版本可能会扩展此功能以支持更多内容
struct WinData
{
long flags;
long extraData;
}
然而,如果你是针对“较旧”的SDK编译的,GetWinData
就没有机会发现你不知道extraData
。如果它无论如何都填充,它将覆盖堆栈上的数据。BOOOM!
这就是为什么在结构体中添加了“由调用者指定大小”的内容,并在末尾附加了新成员。 GetWinData
实现可以检查大小并决定“这个可怜的家伙还不知道所有的新功能”。
这样就可以在API的未来版本中扩展结构,然后Windows可以通过调用者传递的大小知道哪些字段应该被查看或不被查看。这基本上是API版本控制的一种粗略形式。
通常,这些计数字节都以cb
为前缀,代表“字节数”。例如,STARTUPINFO
结构的开头是:
typedef struct _STARTUPINFO {
DWORD cb;
LPTSTR lpReserved;
...
} STARTUPINFO, *LPSTARTUPINFO;
此前,STARTUPINFOEX
结构已被扩展,它包含相同的第一部分但不同的尺寸。根据 cb
的值,Windows 将知道是否查看新的 lpAttributeList
字段。
这个问题是匈牙利命名法的一个例子,基本上是一种将变量类型编码到其名称中的方案。
因为Win32 API是一个C API - 而不是C ++,所以无法使用面向对象的方法扩展API。使用C ++ API,新功能将使用从旧功能派生的结构,并调用接口以获取基本结构,确保类型安全。
C是一种过程式语言,对于结构体的操作更加有限。
这样,未来的版本可以添加额外的字段,并仍能提供向后二进制兼容性:
// CAUTION - the code is not 100% accurate and can fail due to packing rules.
// For illustrative purposes only
if (offsetof(struct foo, field) > f->pbFormat)
{
// called with a version that predates the addition of
// field so revert to a default value
}
struct foo { int size; short s1; char c1; }
开始,然后将其扩展为 struct foo { int size; short s1; char c1; char c2; }
。两者的大小都为8,因此您无法确定调用方是否具有带有或不带有字段 c2
的定义。 - R Samuel Klatchkostruct foo
第二个版本的有效方式。第一个DWORD必须设置为sizeof(object),在v1和v2之间必须有所不同。 - MSalters