Magnus
Magnus

Reputation: 4714

Why put size of struct in the struct?

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

Answers (2)

Jim Buck
Jim Buck

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

Mario
Mario

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

Related Questions