假设你是一名开发人员,正在创建 Windows API。您定义了一些 API 调用,并已将其记录和发布到操作系统中。许多当前 API 调用接受结构体指针作为输入参数,以便在没有大量输入参数的情况下传递许多输入值。
现在,其他开发人员开始为您的操作系统编写代码。
几年后,您决定创建 Windows 操作系统的新版本。但是,您有一些要求:
1. 先前版本的程序必须仍然在新版本上执行 -(API 必须向后兼容)。
2. 您想扩展您的 API -(添加新的 API 调用)。
3. 您希望允许开发人员使用他们为旧版 Windows 编写的现有代码,并允许他们对新操作系统进行编译和执行。
好的,为了使您的旧程序正常工作,您的新 API 必须具有相同的例程和相同的参数等。
那么如何扩展您的 API?您可以添加新的 API 调用,但是如果同时想要使用旧代码并使用一些新的花哨功能而又不想更改太多代码该怎么办呢?
通常API例程需要很多信息,但是创建具有许多形式参数的例程不方便。这就是为什么经常有一个形式参数是指向包含要传递给例程的属性的结构体的指针。这使得API扩展变得容易。例如:
您的旧代码:
struct abc
{
int magicMember;
int a;
int b;
int c;
};
void someApiCall( struct abc *p, int blaBla );
现在,如果您决定通过提供更多信息来扩展您的“someApiCall”,而不改变例程的签名,您只需要更改您的结构。
您的新代码:
struct abc
{
int magicMember;
int a;
int b;
int c;
int new_stuff_a;
int new_stuff_b;
};
void someApiCall( struct abc *p, int blaBla );
您已经保留了例行程序的签名,同时允许旧代码和新代码都能够工作。唯一的秘密是
magicMember,您可以将其视为结构体的修订号,或者 - 如果在新版本中只是添加了新成员 - 结构体的大小。无论哪种方式,您的“someApiCall”都能够区分两种“相同”的结构体,并且您可以从旧代码和新代码中执行该API调用。
如果有人挑剔的话,他可能会说这些不是相同的结构体。确实如此。它们只是具有相同的名称,以防止更多的代码更改。
有关真实示例,请查看
RegisterClassEx API调用和
WNDCLASSEX所需的结构体。