2011年03月25日

MONITORINFOEXはMONITORINFOを継承していた

Win32APIには、それ自身の大きさをメンバにもつ構造体というのが多くあります。例えばウインドウ作成時に使用するWNDCLASSEXは次のようになっています。

WNDCLASSEX
  1. typedef struct {
  2.     UINT cbSize;
  3.     UINT style;
  4.     WNDPROC lpfnWndProc;
  5.     int cbClsExtra;
  6.     int cbWndExtra;
  7.     HINSTANCE hInstance;
  8.     HICON hIcon;
  9.     HCURSOR hCursor;
  10.     HBRUSH hbrBackground;
  11.     LPCTSTR lpszMenuName;
  12.     LPCTSTR lpszClassName;
  13.     HICON hIconSm;
  14. } WNDCLASSEX, *PWNDCLASSEX;

cbSizeが構造体の大きさを表すメンバで、このメンバは必ず最初に宣言されるという決まりがあります。

このメンバは本質的にはバージョンを表していて、将来的に構造体が拡張されてもこのメンバを見ればどのバージョンの構造体を渡されたのか判別できるようになっています。

実際に拡張された例としては、BITMAPINFOHEADERがあります。Windows95で拡張されてBITMAPV4HEADERとなり、Windows98でBITMAPV5HEADERとなりました。

cbSizeを持つ構造体の初期化には、次のような定番の構文があります。

  1. WNDCLASSEX wc = { sizeof(WNDCLASSEX) };

これは配列や構造体を初期化リストを使って初期化する構文で、足りないメンバは0と見なすという性質を利用して、cbSizeに構造体の大きさを入れつつ残りは0で初期化する便利な書き方です。この一行だけで次のコードと等価になります。

  1. WNDCLASSEX wc;
  2. ZeroMemory(&wc, sizeof(WNDCLASSEX));
  3. wc.cbSize = sizeof(WNDCLASSEX);

今回私はMONITORINFOEXを使うことになり、この構造体もcbSizeを含んでいるので次のように書きました。

  1. MONITORINFOEX mi = { sizeof(MONITORINFOEX) };

ところがこれはコンパイルエラーになってしまいました。

最近C++を使う機会が少なかったので、「構文間違えたか?」と思ったのですが、何度見直しても合ってます。これはMONITORINFOEXの方が怪しいと思い定義を見てみると、次のようになっていました。

MONITORINFO, MONITORINFOEXの定義
  1. typedef struct tagMONITORINFO
  2. {
  3.     DWORD   cbSize;
  4.     RECT    rcMonitor;
  5.     RECT    rcWork;
  6.     DWORD   dwFlags;
  7. } MONITORINFO, *LPMONITORINFO;
  8.  
  9. #ifdef __cplusplus
  10. typedef struct tagMONITORINFOEXA : public tagMONITORINFO
  11. {
  12.     CHAR        szDevice[CCHDEVICENAME];
  13. } MONITORINFOEXA, *LPMONITORINFOEXA;
  14. typedef struct tagMONITORINFOEXW : public tagMONITORINFO
  15. {
  16.     WCHAR       szDevice[CCHDEVICENAME];
  17. } MONITORINFOEXW, *LPMONITORINFOEXW;
  18. #ifdef UNICODE
  19. typedef MONITORINFOEXW MONITORINFOEX;
  20. typedef LPMONITORINFOEXW LPMONITORINFOEX;
  21. #else
  22. typedef MONITORINFOEXA MONITORINFOEX;
  23. typedef LPMONITORINFOEXA LPMONITORINFOEX;
  24. #endif // UNICODE
  25. #else // ndef __cplusplus

なんと、MONITORINFOEXはMONITORINFOを継承して定義されていました。

C++で初期化リストが使えるのはaggregate(アグリゲート)なクラス・配列だけで、aggregateであるクラスの条件に基本クラスを持たないというのがあります。MONITORINFOEXは基本クラスを持っているのでaggregateではなく、故に初期化リストを使った初期化ができません。

今後もこういう形で定義される構造体が増えると、初期化リストで初期化できる構造体とそうでない構造体が混在して面倒ですね。MSDNライブラリには継承しているかの情報はないので、コンパイルしてみないと分かりません。

C言語用の定義も気になる

MONITORINFOEXのC言語用の定義を見たら、こちらも気になりました。

  1. #else // ndef __cplusplus
  2. typedef struct tagMONITORINFOEXA
  3. {
  4.     MONITORINFO;
  5.     CHAR        szDevice[CCHDEVICENAME];
  6. } MONITORINFOEXA, *LPMONITORINFOEXA;
  7. typedef struct tagMONITORINFOEXW
  8. {
  9.     MONITORINFO;
  10.     WCHAR       szDevice[CCHDEVICENAME];
  11. } MONITORINFOEXW, *LPMONITORINFOEXW;
  12. #ifdef UNICODE
  13. typedef MONITORINFOEXW MONITORINFOEX;
  14. typedef LPMONITORINFOEXW LPMONITORINFOEX;
  15. #else
  16. typedef MONITORINFOEXA MONITORINFOEX;
  17. typedef LPMONITORINFOEXA LPMONITORINFOEX;
  18. #endif // UNICODE
  19. #endif

最初のメンバとしてMONITORINFO;と型名しか書かれていません。

構造体の名前を宣言しない無名構造体というのはたまに見ますが、メンバの名前を宣言しないというのは初めて見た気がします。

この書き方はMicrosoftの拡張構文でしょうか?検索しようと思ったのですが、この書き方を表す適切なキーワードが思いつきませんでした。

タグ:Win32API
2011年03月25日 【プログラミング】 | コメント(2) |

この記事へのトラックバック
この記事へのコメント

> この書き方はMicrosoftの拡張構文でしょうか?検索しようと思ったのですが、この書き方を表す適切なキーワードが思いつきませんでした。

Microsoft拡張で nameless structure と言うみたいです。
http://msdn.microsoft.com/ja-jp/library/11edsexz.aspx
の一番下のあたりにあります。
Posted by 青柳臣一 at 2011年03月25日

情報ありがとうございます。
構造体のtagがないのがanonymous structureで、こっちはnameless structureなんですね。
似たような言葉で、日本語に訳そうと思うと難しいなぁ。
Posted by 新坂 at 2011年03月25日

コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。