多くのWindowsアプリケーションはそのGUIな性質から、アプリケーションの処理内容を 決定するデータをユーザー入力によって取得すると思われますが、 中にはアプリケーションの起動と共にユーザー入力のデータが必要になることもあります。 このような場合、一般的にはコマンドライン上でアプリケーションのexeファイルを入力し、 その引数として一連のデータを入力する方法が知られています。
C:\>sample.exe use admin
上の例では、C:\に存在するsample.exeにuseとadminという2つのデータを渡しています。 main関数から始まるコンソールアプリケーションではその引数を参照することによって、 入力されたデータをargvで、データの数をagrcで取得することができましたが、 WindowsアプリケーションのWinMainには、このようなコマンドライン引数はありません。 WinMainにあるのは、lpszCmdLineという引数を分けていないコマンドライン文字列であり、 その値は先の2つのデータの例で述べると、次のようになります。
""use" "admin""
各引数は、ダブルクォーテーションで区切られ、それらの間には1個のスペースが入ります。 この規則性を利用すれば、独自のアルゴリズムによって各引数を取り出すことも可能ですが、 できればもっと簡便な方法で引数を取得したいものです。 これはあまり推奨したくない方法ですが、コンソールアプリケーションの開発で幾度か インクルードしていたstdlib.hには、コマンドライン引数用の__argvと__argcという グローバル変数が定義されています。 これらの引数は、WinMainが呼ばれる以前に初期化されることになっているため、 Windowsアプリケーションにとってのコマンドライン引数の役割を果たすことができます。 次に、コード例を示します。
#include <windows.h> int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow) { int i; TCHAR szBuf[256]; for (i = 0; i < __argc; i++) { wsprintf(szBuf, TEXT("%d番目の引数"), i + 1); MessageBox(NULL, __argv[i], szBuf, MB_OK); } return 0; }
__argvはポインタ配列として機能し、添え字によって各引数にアクセスすることができますが、 この最初の引数に関しては常にアプリケーションのexeファイル名になっています。 これは、そもそもコマンドライン文字列がそういった形式になっているからであり、 lpszCmdLineが指す文字列は、正規のコマンドライン文字列からexeファイルの名前を 明示的に取り除いたものに過ぎません。 __argvの初期化に利用されているのは、この正規のコマンドライン文字列であるため、 exeファイル名が含まれているのは不思議なことではないのです。 正規のコマンドライン文字列は、GetCommandLineで取得できます。
LPTSTR GetCommandLine(VOID);
関数の戻り値が、コマンドライン文字列となります。
プログラムをUNICODEとしてコンパイルしている場合に限りますが、 CommandLineToArgvWを利用したコマンドライン引数の取得方法もあります。 この関数は、次のように定義されています。
LPWSTR * CommandLineToArgvW( LPCWSTR lpCmdLine, int *pNumArgs );
lpCmdLineは、GetCommandLineの戻り値を指定します。 pNumArgsは、コマンドライン文字列に存在する引数の数が返ります。 戻り値は、コマンドライン引数のポインタ配列で、 不要になった場合はLocalFreeで開放することになります。
今回のプログラムは、CommandLineToArgvWを呼び出して、 アプリケーションに入力されたコマンドライン引数を表示します。 exeファイルに何らかのファイルをドロップすることは、exeファイルの起動を促すと共に、 そのファイルのフルパスがコマンドライン文字列に含まれることを意味するため、 コマンドプロンプトを起動するのが煩わしい場合は、この方法を採るとよいでしょう。
#ifndef UNICODE #define UNICODE #endif #include <windows.h> int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow) { int i; int nArgs; TCHAR szBuf[256]; LPTSTR *lplpszArgs; lplpszArgs = CommandLineToArgvW(GetCommandLine(), &nArgs); for (i = 0; i < nArgs; i++) { wsprintf(szBuf, TEXT("%d番目の引数"), i + 1); MessageBox(NULL, lplpszArgs[i], szBuf, MB_OK); } LocalFree(lplpszArgs); return 0; }
#ifndef UNICODEによって、UNICODEが定義されていないかを確認し、 もしそうである場合はUNICODEを定義するようにしています。 コードの内容は基本的に、先に示した___argvのコードと変わるところはありません。 プログラムをUNIODEとして開発する場合は、CommandLineToArgvWは便利なツールとなるでしょう。