コマンドラインの取り出し方について(__argvの使い方)

[戻る]
もし 2009/04/07(火) 15:32:16
VC++2008 を使っています。
アプリクラスのInitInstance()にて、コマンドラインを取り出したく、
ネットでいろいろ見て、下記のようなコードを描いてみました。

TCHAR    szStrTmp[1024+1];
for (i = 0; i < __argc; i++) {
 wsprintfW(szStrTmp, _T("%s"), __argv[i]);  // ★1
}

ですが、★1のところでアクセス違反が起きてしまいます。
どうしてでしょうか。
どうしたらよいのでしょうか。

よろしくお願いします。

tetrapod 2009/04/07(火) 16:19:45
http://msdn.microsoft.com/ja-jp/ms647550(en-us).aspx
を見るとダメな理由が書いてあるわけだ。回避策も書いてある、と。

もし 2009/04/07(火) 16:29:51
教えていただいたURLが英語でわからなかったので、下記日本語を見つけて読んでみまし
た。

http://msdn.microsoft.com/ja-jp/library/cc364872.aspx

しかし、駄目な理由と回避策を理解できません。
知識・理解力不足で誠に恐縮ですが、もう少し詳しくご教授願いたく
よろしくお願いします。

もし 2009/04/07(火) 16:43:41
考えてみて、__argv[i]が悪いのかと、下記のように ★2 のNULL判定を入れてみまし
た。
実行した結果、★2 のところで「アクセス違反が発生しました」の例外に飛んで島しま
した。

TCHAR    szStrTmp[MAX_PATH];
for (int i = 1; i < __argc; i++) {
 if (__argv[i] != NULL)            // ★2
 {
  wsprintfW(szStrTmp, _T("%s"), __argv[i]); // ★1
 }
}

上記状況が分かっただけで、どうしたら良いのかは分かっていません。
引き続き、アドバイスをよろしくお願いします。

tetrapod 2009/04/07(火) 16:47:48
アクセス違反に関係なくダメなところからいこう。
提示コードは TCHAR の原則に反している。
・TCHAR と wsprintf とをペアで使う
・char と wsprintfA とをペアで使う
・wchar_t と wsprintfW とをペアで使う
なので TCHAR を使うなら wsprintf を使うべし。
wsprintfW を使うのなら TCHAR でなく wchar_t を使うべし。

次に MSDN の説明「バッファの最大サイズは 1024 バイトです」に反している。
マルチバイト文字セットを指定しているなら TCHAR は char なので
szStrTmp は 1025 バイトになり、違反。
ユニコード文字セットを指定しているなら TCHAR は wchar_t なので
szStrTmp は 2050 バイトになり、違反。

回避策は MSDN の同じページに載っているわけでその辺は自力で探して味噌。
別の方策としてはバッファサイズを 1024 バイト以下にする、ってのもあり。

んで、俺がわざわざ英語版ページを張っておいたのは、日本語ページに無い
重要な注意事項の記述が英語版ページにあるからなんだが、読んでないだろう?
wsprintf にフォーマット文字 "%s" を指定してしまうと
バッファオーバーラン脆弱性が発生してしまうぞ、とある。
(他にも注意事項がある)
この関数を使うべきか、使ってよいか、使うならどう使うべきか、要再考。

とおりすがり 2009/04/07(火) 16:49:20
環境が書いてないから何ともいえないけどAppのInitInstanceが使えるということで推測。
CCommandLineInfoつかえば?

もし 2009/04/07(火) 17:12:01
tetrapodさん、とおりすがりさん

アドバイス、ありがとうございます。

>・TCHAR と wsprintf とをペアで使う
まず、これを直しました。

>次に MSDN の説明「バッファの最大サイズは 1024 バイトです」に反している。
とりあえず、1024にしてみました。
(回避策についてはこれを送った後よく読んで探してみます。)

上記状態で実行し、やはり ★2 で飛んでしまいます。

英語版理解できず、アドバイスありがとうございます。
バッファオーバーラン脆弱性が発生や他にも注意事項があるとの事。

理解しきれず使うのはよくないので、とおりすがりさんの CCommandLineInfoを使うとの
アドバイスを受け、一度こちらを調べてみようと思います。

subaru 2009/04/07(火) 17:13:51
ユニコード文字セットの場合、引数は __wargv に設定される。
つまりTCHARを使うなら、__argv ではなくて __targv にするべきです。

subaru 2009/04/07(火) 17:29:04
訂正します。

>ユニコード文字セットの場合、引数は __wargv に設定される。

エントリポイントがwWinMainの場合です。
(ユニコード文字セットでもエントリポイントがWinMainの場合もあり得るので・・)

もし 2009/04/07(火) 17:46:32
[[解決]]
とおりすがりさんのアドバイスを受け、調べた結果、下記で自分のやりたいことができ
るようになりました。

皆さん、ありがとうございました。

int    i;
int    nArgs;
LPTSTR *lplpszArgs;

lplpszArgs = CommandLineToArgvW(GetCommandLine(), &nArgs);

CString sss2;
for (i = 1; i < nArgs; i++) {
    PCTSTR aaa = lplpszArgs[i];
}

ZCH 2009/04/08(水) 01:35:41
> szStrTmp は 1025 バイトになり、違反。

変換後のサイズが1024バイトまで保証という意味だと思います。
引数に渡すszStrTmpの配列長は何バイトでもよいのでは?

古いスレッドには追加発言できません。