Reputation: 4714
I've for the first time begun using SetupAPI on Windows and notice that I always need to put the size of a struct into the struct before calling a function that populates the struct. For instance:
SP_DEVICE_INTERFACE_DATA dintf_data;
dintf_data.cbSize = sizeof(dintf_data);
SetupDiEnumDeviceInterfaces(di, NULL, &GUID_DEVINTERFACE_USB_DEVICE, 0, &dintf_data);
What's the reason for doing that?
Is it a good way to achieve whatever it is they are trying to achieve, or are there better ways?
(The only reason I can come up with is that it's a way to deal with API versioning.)
Upvotes: 0
Views: 188
Reputation: 20726
Yup, you got it, API versioning.
The OS checks the size passed in to determine which version of the structure you are using. As features are added for newer versions of the API, fields are added (never removed), to increase the size, so the OS can know easily exactly which version you are using.
Upvotes: 5
Reputation: 36487
It's a way of versioning to ensure backwards (and forward!) compatibility.
Imagine you've got some struct
for a message box:
struct MessageBoxData {
const char *message;
const char *caption;
};
You compile this into your API and it uses this to display a message box with a message
and a caption
. You're fine.
Later on, you release API version 2, which adds another field for the icon displayed:
struct MessageBoxData {
const char *message;
const char *caption;
unsigned char icon;
};
Let's assume the order of elements isn't magically padded or restructured by the compiler and any program may pass this more complex API version 2 struct to the API version 1 library without issues.
However, what happens the other way around? Using a program compiled against API version 1 with a library of API version 2.
The library will try to read icon
, which will result in a read outside the valid boundaries of this struct
!
In a similar way there might be problems if you try to copy the contents with memcpy()
for example.
To avoid this, the Windows API typically expects the very first member to define the actual size of the struct. In this example you could create something like this:
// Version 1
struct MessageBoxData {
unsigned int size;
const char *message;
const char *caption;
};
// Version 2
struct MessageBoxData {
unsigned int size;
const char *message;
const char *caption;
unsigned char icon;
};
You might think that they could just use sizeof()
in the called function. While this is theoretically true, it won't solve the problem of different definitions between compile times (of library and program).
Upvotes: 2