あなたが正しい方法でやろうとしていることをするために、あなたが考慮に入れる必要があるいくつかの重要なことがあります。ここであなたのためにそれらを分解するために最善を尽くします。
MSDNのcount
関数のドキュメントからのパラメーターの定義から始めましょう:wcstombs()
マルチバイト出力文字列に格納できる最大バイト数。
これは、ワイド文字入力文字列のワイド文字の数については何も述べていないことに注意してください。サンプルの入力文字列( "New Text File.txt")のすべてのワイド文字はシングルバイトのASCII文字として表すことができますが、入力文字列の各ワイド文字が出力で正確に1バイトを生成するとは限りません。考えられるすべての入力文字列の文字列(このステートメントで混乱する場合は、Unicodeと文字セットに関するJoelの記事を確認してください)。したがって、wcstombs()
出力バッファのサイズを渡すと、入力文字列の長さをどのように知ることができますか?ドキュメントには、標準のC言語規則に従って、入力文字列はnullで終了することが期待されていると記載されています。
wcstombsは、カウントが発生する前または発生したときにワイド文字のヌル文字(L'\ 0')に遭遇すると、それを8ビット0に変換して停止します。
これはドキュメントに明示的に記載されていませんが、入力文字列がnullで終了していない場合、出力文字列にバイトwcstombs()
が書き込まれるまでワイド文字を読み取り続けると推測できます。count
したがって、nullで終了していないワイド文字列を処理している場合は、入力文字列の長さを知るだけでは不十分です。どういうわけか、出力文字列が必要なバイト数を正確に把握し(変換を行わずに決定することは不可能です)、それをcount
パラメーターとして渡して、目的の処理をwcstombs()
実行する必要があります。
なぜ私はこのnull終了の問題にそれほど焦点を合わせているのですか?MSDNのFILE_NOTIFY_INFORMATION
構造体のドキュメントには、そのFileName
フィールドについて次のように書かれているためです。
ディレクトリハンドルに関連するファイル名を含む可変長フィールド。ファイル名はUnicode文字形式であり、nullで終了していません。
フィールドがnullで終了していないという事実FileName
は、デバッガーでフィールドを見ると、フィールドの最後に「不明な漢字」がたくさんある理由を説明しています。構造のドキュメントには、この分野FILE_NOTIFY_INFORMATION
に関するもう1つの知恵も含まれています。FileNameLength
レコードのファイル名部分のサイズ(バイト単位)。
これは文字ではなくバイトを示していることに注意してください。したがって、入力文字列の各ワイド文字が出力文字列に正確に1バイトを生成すると想定したい場合でも、;を渡してはなりません。渡す必要があります(または、もちろん、nullで終了する入力文字列を使用します)。このすべての情報をまとめると、元の呼び出しが失敗した理由を最終的に理解できます。文字列の終わりを超えて読み取り、無効なデータをチョークしていました(これによりエラーがトリガーされました)。fileInfo.FileNameLength
count
fileInfo.FileNameLength / sizeof(WCHAR)
wcstombs()
EILSEQ
問題を解明したので、次に考えられる解決策について話し合います。これを正しい方法で行うために、最初に知っておく必要があるのは、出力バッファの大きさです。幸いなことに、ドキュメントには、wcstombs()
ここで役立つ最後のヒントが1つあります。
mbstr引数がNULLの場合、 wcstombsは必要なサイズを宛先文字列のバイト単位で返します。
したがって、この関数を使用する慣用的な方法は、wcstombs()
関数を2回呼び出すことです。1回目は出力バッファーの大きさを決定し、2回目は実際に変換を行います。最後に注意すべきことは、前に述べたように、少なくとも最初の。への呼び出しでは、ワイド文字の入力文字列をnullで終了する必要があるということwcstombs()
です。
これをすべてまとめると、これがあなたがやろうとしていることを実行するコードのスニペットです:
size_t fileNameLengthInWChars = fileInfo.FileNameLength / sizeof(WCHAR); //get the length of the filename in characters
WCHAR *pwNullTerminatedFileName = new WCHAR[fileNameLengthInWChars + 1]; //allocate an intermediate buffer to hold a null-terminated version of fileInfo.FileName; +1 for null terminator
wcsncpy(pwNullTerminatedFileName, fileInfo.FileName, fileNameLengthInWChars); //copy the filename into a the intermediate buffer
pwNullTerminatedFileName[fileNameLengthInWChars] = L'\0'; //null terminate the new buffer
size_t fileNameLengthInChars = wcstombs(NULL, pwNullTerminatedFileName, 0); //first call to wcstombs() determines how long the output buffer needs to be
char *pFileName = new char[fileNameLengthInChars + 1]; //allocate the final output buffer; +1 to leave room for null terminator
wcstombs(pFileName, pwNullTerminatedFileName, fileNameLengthInChars + 1); //finally do the conversion!
もちろん、電話をすることを忘れないでください、delete[] pwNullTerminatedFileName
そしてdelete[] pFileName
、あなたがそれらを片付け終わったら。
最後に一つだけ
この答えを書いた後、私はあなたの質問をもう少し詳しく読み直し、あなたが犯しているかもしれない別の間違いについて考えました。最初の2文字( "Ne")を変換しただけで失敗すると言いwcstombs()
ます。これは、最初の2つのワイド文字の後に入力文字列の初期化されていないデータがヒットしていることを意味します。代入演算子を使用して、あるFILE_NOTIFY_INFORMATION
変数を別の変数にコピーしましたか?例えば、
FILE_NOTIFY_INFORMATION fileInfo = someOtherFileInfo;
これを行うと、toの最初の2つのワイド文字のみがコピーsomeOtherFileInfo.FileName
されfileInfo.FileName
ます。これが当てはまる理由を理解するために、FILE_NOTIFY_INFORMATION
構造体の宣言を検討してください。
typedef struct _FILE_NOTIFY_INFORMATION {
DWORD NextEntryOffset;
DWORD Action;
DWORD FileNameLength;
WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
FileName
コンパイラーが代入操作のコードを生成するとき、可変長フィールドであることに引っ張られているトリックを理解しないので、sizeof(FILE_NOTIFY_INFORMATION)
バイトをからsomeOtherFileInfo
にコピーするだけfileInfo
です。FileName
は1の配列として宣言されているため、1WCHAR
文字だけがコピーされると思いますが、コンパイラは構造体を2バイトの長さにパディングします(その長さはのサイズの整数倍になりますint
)。 1秒WCHAR
もコピーされるのはそのためです。