开发者

Why do structures need to be told how big they are?

I've noticed that in c/c++ a lot of Win32 API structs need to be told how big they are. i.e someStruct.pbFormat = sizeof(SomeStruct)开发者_JAVA技巧

Why is this the case? Is it just for legacy reasons? Also any idea what "pb" stands for too?

EDIT: oops, yeah I meant "cbFormat"


This is for backward compatibility when Windows API is extended.

Imagine the following declarations

struct WinData
{
   long flags;
}
BOOL GetWinData(WinData * wd);

which you are calling like this:

WinData wd;
GetWinData(&wd);

A future OS version may extend this to

struct WinData
{
   long flags;
   long extraData;
}

However, if you have compiled against the "older" SDK, GetWinData has no chance to figure out you don't know about extraData. If it would fill it in regardless, it would overwrite data on the stack. BOOOM!

That's why, the "size known to the caller" is added to the structure, and new members are appended at the end. The GetWinDataimplementation can inspect the size and decide "this poor guy doesn't know about all the new features yet".


This is so that the structure can be extended in future versions of the API, and Windows can then know (by the size the caller passed in) which fields should be looked at or not. It's basically a crude form of API versioning.

Usually those count bytes are prefixed with cb, which stands for "count of bytes". For example, the STARTUPINFO structure starts with:

typedef struct _STARTUPINFO {
  DWORD  cb;
  LPTSTR lpReserved;
  ...
} STARTUPINFO, *LPSTARTUPINFO;

This was extended at some point with the STARTUPINFOEX structure, which contains the same first part but a different size. Depending on the value of cb, Windows will know whether or not to look at the new lpAttributeList field.


The pb is an example of Hungarian Notation, basically a scheme for encoding a variable type into its name.


Because Win32 API is a C API - not C++, so object orientated methods of extending the API are not available. With C++ API a new feature would use a structure that derives from the older one and call an interface that take the base structure, ensuring type safety.

C is a procedural language and is more limited in what you can do with structures.


So that future versions can add extra fields and still be able to provide backwards binary compatibility:

// 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
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜