あなたが Windows API を作成する開発者であると想像してください。いくつかの API 呼び出しのセットが定義され、文書化され、OS がリリースされました。現在の API 呼び出しの多くは、構造体へのポインターを入力引数として受け入れ、膨大な量の入力引数を持たずに多くの入力値を渡すことができます。
これで、開発者はあなたの OS 用のコードを書き始めます。
数年後、Windows OS の新しいバージョンを作成することにしました。ただし、いくつかの要件があります。
- 以前の OS バージョン用にコンパイルされたプログラムは、新しい OS でも実行する必要があります (そのためには、API に下位互換性が必要です)。
- API を拡張したい - (新しい API 呼び出しが追加されました)。
- 開発者が既存のコード (古いウィンドウ用に記述したもの) を使用できるようにし、新しい OS でコンパイルして実行できるようにしたいと考えています。
OK - 古いプログラムが動作するためには、新しい API に同じ引数を持つ同じルーチンが必要です。
API を拡張するにはどうすればよいでしょうか。新しい API 呼び出しを追加することはできますが、同時に、古いコードを使用し、コードに多くの変更を加えずに新しいファンシーな機能を使用したい場合はどうでしょうか?
通常、API ルーチンは多くの情報を必要としますが、多くの仮引数を持つルーチンを作成するのは不便です。そのため、仮引数の 1 つが、ルーチンに渡したいプロパティを含む構造体へのポインターであることがよくあります。これにより、API の拡張が容易になります。例えば:
古いコード:
struct abc
{
int magicMember; // ;-)
int a;
int b;
int c;
};
void someApiCall( struct abc *p, int blaBla );
ルーチンの署名を変更せずに詳細情報を提供して「someApiCall」を拡張することにした場合は、構造を変更するだけです。
あなたの新しいコード:
// on new OS - defined in a header with the same name as older OS
// hence no includes changes
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」は 2 種類の「同じ」構造体を区別でき、古いコードと新しいコードの両方からその API 呼び出しを実行できます。
うるさい人なら、これらは同じ構造ではないと言うかもしれません。確かにそうではありません。それ以上のコード変更を防ぐために、それらは同じ名前になっています。
実際の例については、 RegisterClassEx API 呼び出しとWNDCLASSEX 構造体を確認してください。