Visual C++ 2015 でマルチバイト文字セットを利用するとコマンドライン引数を正しく取得できない

こんにちは、Visual Studioサポートチームです。

今回は、Visual Studio 2015 で マルチバイト文字セット (MBCS) を利用する際に確認されている問題についてご案内します。

 

現象

マルチバイト文字セット (MBCS) を利用した Visual C++ アプリケーションにおいて、コマンドラインの引数の最初の文字にマルチバイト文字がある場合に、WinMain 関数内で正しく引数を取得できない事象が確認されています。

引数の最初の文字が 1 バイト文字である場合には、正しく引数を得られますが、引数に漢字などのマルチバイト文字が利用されている場合、正しく引数を取得することができない場合があります。

マイクロソフトでは、本現象を Visual C++ 2015 のランタイム ライブラリ (ユニバーサル CRT) の問題として認識しており、調査を行っております。

 

対処方法

本現象は MBCS 固有の問題であるため、プロジェクトで使用する文字セットとして Unicode を利用した場合は、本現象は発生しません。

Unicode を利用することができない場合は、別途、GetCommandLine 関数を使用して引数を取得することができます。 なお、MBCS は既に非推奨となっているため、可能な限り Unicode への移行をご検討ください。

 

マルチバイト文字セット (MBCS) のサポート
(日本語訳) https://msdn.microsoft.com/ja-jp/library/5z097dxa.aspx
(英語原文) https://msdn.microsoft.com/en-us/library/5z097dxa.aspx

 

MBCS のまま対応する方法として、対応例 を2つ紹介します。

 

対応例 1

プログラムのエントリ ポイントを Unicode 版の wWinMain に変更することで、引数を正しく取得することができます。

wWinMain は、プロジェクトのプロパティで「マルチバイト文字セットを使用する」を選択している場合でも利用することができます。

 

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,

_In_opt_ HINSTANCE hPrevInstance,_In_ LPWSTR lpCmdLineW,_In_ int nCmdShow)

{

// 変換後のサイズを確認int iCharSize = WideCharToMultiByte(CP_ACP, 0, lpCmdLineW, -1, NULL, 0, NULL, NULL);// 変換後の領域を確保LPSTR lpCmdLine = new char[iCharSize];// 文字コードの変換WideCharToMultiByte(CP_ACP, 0, lpCmdLineW, -1, lpCmdLine, iCharSize,NULL, NULL );// 変換した文字を表示MessageBox((HWND) NULL, (LPSTR)lpCmdLine, "メッセージ", MB_APPLMODAL|MB_ICONSTOP|MB_OK);// 領域の削除delete lpCmdLine;return 0;

}

 

対応例 2

MFC をご利用の場合、上記のように wWinMain を利用することができませんが、InitInstance() の冒頭で m_lpCmdLine を置き換える方法を利用することができます。

以下に、引数を置き換える関数と、利用方法の例をご案内します。  

引数を置き換える関数の例)

LPTSTR fnReplaceCmdLine(LPTSTR lpCmdLine)
{

LPTSTR lpRet = lpCmdLine; // 戻り値// 前処理LPTSTR lpCmdLineTmp = GetCommandLine(); // コマンドライン取得CString strCommand = lpCmdLineTmp; int iLength = strCommand.GetLength(); // 文字列の長さ// プログラム名の検索int nArgs = 0;LPWSTR lpComdLineW = GetCommandLineW();LPWSTR *lpArgListW = CommandLineToArgvW(lpComdLineW, &nArgs);int iPointer = WideCharToMultiByte(CP_ACP, 0, lpArgListW[0], -1, NULL, 0, NULL, NULL); // 変換後のサイズを確認し、プログラム名の長さとする。// 引数の検索if (lpCmdLineTmp[iPointer] == '"') iPointer++; // プログラム名が " で囲まれていた場合は、削除while (iPointer < iLength && lpCmdLineTmp[iPointer] == ' ') iPointer++; // ブランク削除if (iPointer < iLength) // 返す文字があるか

lpRet = lpCmdLineTmp + iPointer;

 

// 後処理LocalFree(lpArgListW);return lpRet;

}

 

上記関数の利用例)

m_lpCmdLine = fnReplaceCmdLine(m_lpCmdLine); // ANSIの文字化け対策  

 

 

Visual C++ 2015 の問題によりご迷惑をおかけいたしますが、Unicode への移行もしくは、上記対処方法のご利用についてご検討くださいますようお願い申し上げます。