Windows APIプログラミングの概要
Windows APIの呼び出し方
Windows APIは関数の形で提供されており、C言語などのプログラミング言語から呼び出すことができます。
Visual Studio(Visual C++)にはC/C++言語用にwindows.h
というヘッダーファイルが用意されているので、これをインクルードすることで基本的な関数が使えるようになります。
その他のヘッダーは必要に応じてインクルードします。
手順としてはC言語標準関数を使う場合と変わりません。
文字の扱い
C言語ではchar
型で文字を扱うのが基本ですが、他にwchar_t
という型も存在します。
char型で扱う文字をマルチバイト文字、wchar_t型で扱う文字をワイド文字といいます。
(詳しくはマルチバイト文字やワイド文字で説明しています)
Window APIが提供する関数は、同じ機能の関数でもマルチバイト文字版とワイド文字版の二つが用意されています。
マルチバイト文字用の関数にワイド文字を渡すことはできませんし、その逆もできません。
どちらを呼び出すべきかをきちんと把握する必要があります。
(どちらか一方用しか提供されていないものもあります)
TCHAR型
Visual StudioにはTCHAR
型という文字型があります。
TCHAR型はVisual Studioの設定によってコンパイル時に「char型」もしくは「wchar_t型」に置き換えられます。
これはUnicodeに対応しない古い環境とのソースコードの互換性維持のためのものです。
詳しくはTCHAR型を参照してください。
Win98などの大昔のシステムにも対応しなければならない場合はTCHAR型は便利ですが、微妙にタイピング量も増えるので、今現在のシステム用に開発する場合は基本的に使用しなくて良いです。
データ型
Windows APIの関数が使用するデータ型は見慣れないものが多くあります。
特定の関数用の構造体などもありますが、多くは既存の(C言語の)型をtypedefで別名にしたものです。
Windows APIではC言語が用意する型をそのまま使用していることはあまりなく、int型などの基本的なデータ型でさえ別名が存在します。
これはデータの目的に合わせて名前を付けることで意味を明確にする目的があると思われます。
以下はWindows APIにおけるデータ型の一部です。
基本型
- C言語での型名
- Windows APIでの型名
- char
- CHAR
- unsigned char
- UCHAR
BYTE - short
- SHORT
- unsigned short
- USHORT
WORD - int
- INT
BOOL - unsigned int
- UINT
- long
- LONG
- unsigned long
- ULONG
DWORD - float
- FLOAT
- double
- DOUBLE
- void
- VOID
- void*
- LPVOID
- const void*
- LPCVOID
- wchar_t
- WCHAR
上記の型の先頭に「P」または「LP」が付いたものはポインタ型です。
(PUCHAR→unsigned char*、LPINT→int*)
ただしいくつかのデータ型は変形したり、接頭語が付かないものもあります。
(LPCH→char*、LPFLOAT→存在しない)
また、構造体などで単純に単語名が「P」から始まるデータ型はポインタではありません。
例えば「PAINTSTRUCT
」という構造体はポインタ型ではなく、ポインタ型は「PPAINTSTRUCT
」や「LPPAINTSTRUCT
」という名前になります。
PとLPの二種類があるのは16ビット環境時代の名残りで、現在においては違いはありません。
(P→pointer、LP→long pointerの意味)
頭に「P」「LP」が付けばポインタ型、と覚えておきましょう。
BOOL型
はint型の別名として定義されていますが、これは「真」か「偽」かの二通りを表す場合に使用されるデータ型です。
つまり0(偽)か0以外(真)かを表します。
BOOL型を返す関数は、結果が真か偽かが重要な点であって、真の場合にその実際の数値には意味がないことがほとんどです。
真はTRUE
、偽はFALSE
というキーワードで表すことができます。
C++には「bool
型」があり(小文字)、使い方も基本的に同じです。
(true
、false
というキーワードもある。こちらも小文字)
文字列型
- C言語での型名
- Windows APIでの型名
- char*
- PSTR
LPSTR
PCHAR
LPCHAR
PCH
LPCH
NPSTR - const char*
- PCSTR
LPCSTR
PCCH
LPCCH - wchar_t*
- PWSTR
LPWSTR
PWCH
LPWCH
PWCHAR
NWPSTR - const wchar_t*
- PCWSTR
LPCWSTR
PCWCH
LPCWCH - TCHAR*
- PTSTR
LPTSTR
PTCH
LPTCH
PTCHAR - const TCHAR*
- PCTSTR
LPCTSTR
PCTCH
LPCTCH
ただの文字列を表すデータ型にこれだけの数が存在します。
以下のコツさえ覚えておけば、大体どのデータ型の意味かはわかります。
「STR」はstringの略で、文字列の意味です。
「N」はNULL終端の意味で、これも文字列の意味です。
「P」「LP」は基本型の時と同じく、ポインタという意味です。
「C」はconstの意味です。
「W」はワイド文字の意味です。
「T」はTCHAR型の意味です。
つまり「W」「T」が付いているか(無ければマルチバイト文字)、「C」が付いているか、に注目すれば要求されるデータ型が分かります。
(ただしCの次にHが続く場合はCHARの意味なので注意)
「PCHAR」はあるのに「LPCHAR」はないなど規則が統一されておらず、これらの型を使いこなすのは面倒に思えますが、プログラマは通常の文字型(char
型、wchar_t
型)を使用すれば問題ありません。
名前を揃えるために「CHAR」「WCHAR」を使用しても良いです。
(tchar型(TCHAR)を使いたいならばそちらを使う)
例えばポインタ型なら「LPWSTR型」が用意されていますが、これは「WCHAR*」と宣言するのと同じことです。
「LPCWSTR型」なら「const WCHAR*」で同じデータ型になります。
関数の引数などは「LPWSTR」などの型を要求しますが、元のデータ型さえ合っていれば別名で宣言したものを渡しても問題ありません。
(キャストしなければ警告が出ることもあるがエラーにはならない)
その他のデータ型
その他、構造体がよく使われるほか、ハンドルというものがよく登場します。
これらはあらゆるリソース(データ)へのアクセス手段で、実体はポインタです。
ハンドルは専用の関数でシステム(Windows)から取得し、その中身をプログラマがいじることはありません。
(ポインタ演算などで指し示す先を変更しない)
HANDLE型、HDC型、HWND型など、大抵は「H」から始まります。
また、データ型ではありませんが、大量の定数が使用されます。
関数の引数や戻り値などの数値に名前を付けて意味をわかりやすくしています。
Windows APIを使ってみる
前置きが長くなりましたが、実際にWindows APIを使ってみます。
とりあえずC言語のコンソールアプリケーション用のプロジェクトを使用します。
MessageBox関数
Windows APIを使用するにはwindows.h
をインクルードします。
以下のコードをコンパイル&実行すると、いつものコンソール画面と共にメッセージボックスが表示されます。
OKボタンをクリックするとプログラムは終了します。
#include <windows.h>
int main()
{
MessageBoxA(NULL, "メッセージボックスのテスト", "テスト", MB_OK);
//ワイド文字版
//MessageBoxW(NULL, L"メッセージボックスのテスト", L"テスト", MB_OK);
}
たった一行だけのシンプルなコードです。
MessageBox
関数はWindows APIが提供する関数で、画面にメッセージボックスを表示します。
(OKとかYes/Noとかの簡単なメッセージを表示するダイアログ)
MessageBoxA
関数の定義は以下のようになっています。
-
int MessageBoxA(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
); -
ウィンドウhwndをオーナーとするメッセージボックスを表示する。
本文はlpText、キャプション(タイトル)はlpCaption、ボタンやアイコンの種類はuTypeで指定する。
戻り値は選択されたボタンの種類。
サンプルコードで実行しているのはMessageBox関数ではなくMessageBoxA関数であることに注意してください。
意味は後述します。
このページはWindow APIの使い方についての説明のため、MessageBox関数についてはごく基本的な機能の解説に留めます。
詳細はメッセージボックスの項で改めて説明します。
第一引数で要求されるHWNDというデータ型はウィンドウハンドルと言って、ウィンドウを識別するための値です。
ここに親となるウィンドウを指定すると、メッセージボックスは親ウィンドウよりも常に前面に表示されるようになり、メッセージボックスを閉じるまで親ウィンドウは操作不能になります。
必要ない場合はNULL
を指定できます。
今回は使用しないのでNULLを指定します。
メッセージボックスの本文やタイトルはLPCSTR型が要求されています。
これは「LP→ポインタ」「C→const」「STR→文字列」ですから、要するにchar型の文字列のことで、C言語の標準の文字列です。
char型の配列やchar*型(どちらもNULL文字終端)のほか、文字列リテラルをそのまま指定できます。
仮にconstが付かない文字列型が要求されている場合、文字列リテラルを渡すことはできません。
これは関数の内部でデータを書き換える(可能性のある)処理をすることを意味していて、文字列リテラルは書き換えができないのでエラーになります。
constが付いている場合は関数の処理でデータが書き換わらなことが保障されています。
ここにconstが付かないデータ型を渡しても問題ありません。
UINT型はunsigned int型の別名です。
引数uTypeはあらかじめ用意されている定数(の組み合わせ)で、メッセージボックスの状態を指定します。
ここでは「OK」ボタンのみを表示する定数MB_OK
を指定しています。
戻り値
戻り値はただのint型ですが、これにも定数が用意されています。
例えば「OK」ボタンが選択された場合はIDOK
という定数が、キャンセルボタンや右上の閉じるボタンが選択された場合はIDCANCEL
という定数が返されます。
以下は先ほどの引数uTypeをMB_OKCANCEL
に変更して、「OK」「キャンセル」の二つのボタンが表示されるメッセージボックスを作成します。
そして、クリックしたボタンの種類をコンソールに表示します。
#include <stdio.h>
#include <windows.h>
int main()
{
int select = MessageBoxA(NULL,
"本文",
"タイトル",
MB_OKCANCEL); //「OK」「キャンセル」ボタンを表示
switch (select)
{
case IDOK:
printf("OKボタン");
break;
case IDCANCEL:
printf("キャンセルボタン");
break;
default:
printf("不明な閉じ方がされました。\n");
getchar();
return 0;
}
printf("が選択されました。\n");
getchar();
}
キャンセルボタンが選択されました。
ワイド文字版
先ほど使用したMessageBoxA関数はマルチバイト文字を使用してメッセージボックスを表示する関数です。
Aは「ANSI」の意味で、「ANSI C」はC言語の標準規格の名前です。
MessageBox関数にはMessageBoxW関数というワイド文字版も存在します。
最初のサンプルコードにもコメント行で示していますが、ワイド文字版は関数名の最後のAをWに変更することで呼び出すことができます。
#include <windows.h>
int main()
{
MessageBoxW(NULL, L"メッセージボックスのテスト", L"テスト", MB_OK);
}
-
int MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType
); -
ウィンドウhwndをオーナーとするメッセージボックスを表示する。
本文はlpText、キャプション(タイトル)はlpCaption、ボタンやアイコンの種類はuTypeで指定する。
戻り値は選択されたボタンの種類。
第二、第三引数のデータ型がLPCSTRからLPCWSTRに変わっただけで、機能はMessageBoxA関数と同じです。
データ型に「W」があるので、これはワイド文字を要求しています。
ワイド文字(列)は文字(列)リテラルにLプリフィックスを付けることで作成することができます。
ワイド文字を格納する変数はchar型ではなくWCHAR型を使用します。
#include <windows.h>
int main()
{
WCHAR text[] = L"本文";
WCHAR *title = L"タイトル";
MessageBoxW(NULL, text, title, MB_OK);
}
なお、C言語のワイド文字のページではワイド文字の使用の前にsetlocale関数を実行する、と説明しましたが、これはC言語の関数を使用する場合です。
Windows APIの関数はsetlocale関数の影響を受けません。
ワイド文字をC言語の機能で処理する場合はsetlocale関数が必要です。
三種類の同名関数
メッセージボックス表示関数には「MessageBoxA関数」「MessageBoxW関数」の他に「MessageBox関数」も存在します。
(末尾にAもWも付かない)
MessageBox関数はVisual Studioの設定によって呼び出し先がMessageBoxA関数またはMessageBoxW関数に切り替わるようになっています。
その設定はTCHAR型で説明した「文字セット」です。
Visual Studio2015
Visual Studio2019
この設定が「マルチバイト文字セットを使用する」の時、TCHAR型はchar型に置き換えられます。
そしてMessageBox関数はMessageBoxA関数の別名となります。
「Unicode文字セットを使用する」の時、TCHAR型はwchar_t型に置き換えられます。
そしてMessageBox関数はMessageBoxW関数の別名となります。
つまりTCHAR型を使用する場合はこのAもWも付かないMessageBox関数を使用することになります。
#include <tchar.h>
#include <windows.h>
int main()
{
//設定によって呼び出しが
//「MessageBoxA」または「MessageBoxW」に切り替わる
MessageBox(NULL, _T("本文"), _T("タイトル"), MB_OK);
}
この設定はVisual Studio2005あたりから「Unicode文字」が初期状態になったそうです。
(それ以前は「マルチバイト文字」が初期状態)
つまり最近のVisual Studioの場合、AもWも付けない場合はワイド文字版が呼び出されます。
これは他のWindows API関数でも大体同じルールになっています。
TCHAR型は設定を変更することで手軽にマルチバイト文字版とワイド文字版とを切り替えられます。
しかしいまさらUnicode非対応のWindows用にアプリケーションを開発することはほとんどないでしょうし、TCHAR型を使用するメリットは薄いでしょう。
なので設定は「Unicode文字セットを使用する」のままで(初期設定)、関数呼び出しは末尾にはAもWも付けずにワイド文字版の関数を使用するのが無難かと思います。
つまり文字はWCHAR型(wchar_t型の別名)を使用し、文字(列)リテラルにはLプリフィックスを使用して開発を行うことをお勧めします。
当サイトでも基本的にこのルールで解説を行います。
#include <windows.h>
int main()
{
WCHAR text[] = L"本文";
WCHAR *title = L"タイトル";
MessageBox(NULL, text, title, MB_OK);
}
将来的に変更があるかもしれないと不安ならば明示的にWを付けてワイド文字版を使用しても良いと思います。
マルチバイト文字版はどうしてもワイド文字を使用したくない場合に、明示的にAを付けて呼びだすのが良いと思います。
ただしAPIによってはマルチバイト文字版がサポートされていないこともあるようです。
また、文字型に関係のない処理をする関数はA版もW版も存在しません。
Windows APIを直接呼び出して実行することは面倒なことが多いので、ある程度使いやすく作り直された関数やライブラリなどが存在します。
これは内部的にはWindows APIを呼び出していますが、引数や戻り値を変更したり、呼び出しの前後に別の処理を挟んだりすることでプログラマが面倒な処理を書かなくて済むようにしたものです。
こういったものをWindows APIのラッパー関数やラッパーライブラリ(単にラッパーとも)と言います。
クラス機能がある言語ではラッパークラスというものもあります。
(wrapper→ラップする、包み込む)
Windows APIのラッパーライブラリはマイクロソフトによるMFCやALT、WTLなどがあります。
(ただしこれらはC++用)
またC/C++以外からWindows APIを直接呼び出せるようにしたラッパーライブラリもあります。