4

変換に問題がありwchar_t*ます。char*

WinAPI関数によって返される構造体wchar_t*から文字列を取得しているので、文字列は正しいと思います。FILE_NOTIFY_INFORMATIONReadDirectoryChangesW

Visual Studioデバッガーで変数にカーソルを合わせると、wchar文字列が「NewTextFile.txt」であると想定します。「N」といくつかの不明な漢字が表示されます。時計では文字列が正しく表現されていますが。

wcharをcharに変換しようとするとwcstombs

wcstombs(pfileName, pwfileName, fileInfo.FileNameLength);

2文字だけをchar*( "Ne")に変換してから、エラーを生成します。

このブロックの関数_wcstombs_l_helper()でのwcstombs.cの内部エラー:

if (*pwcs > 255)  /* validate high byte */
{
    errno = EILSEQ;
    return (size_t)-1;  /* error */
}

例外としてスローされません。

何が問題になる可能性がありますか?

4

3 に答える 3

15

あなたが正しい方法でやろうとしていることをするために、あなたが考慮に入れる必要があるいくつかの重要なことがあります。ここであなたのためにそれらを分解するために最善を尽くします。

MSDNcount関数のドキュメントからのパラメーターの定義から始めましょう:wcstombs()

マルチバイト出力文字列に格納できる最大バイト数。

これは、ワイド文字入力文字列のワイド文字の数については何も述べていないことに注意してください。サンプルの入力文字列( "New Text File.txt")のすべてのワイド文字はシングルバイトのASCII文字として表すことができますが、入力文字列の各ワイド文字が出力で正確に1バイトを生成するとは限りません。考えられるすべての入力文字列の文字列(このステートメントで混乱する場合は、Unicodeと文字セットに関するJoelの記事を確認してください)。したがって、wcstombs()出力バッファのサイズを渡すと、入力文字列の長さをどのように知ることができますか?ドキュメントには、標準のC言語規則に従って、入力文字列はnullで終了することが期待されていると記載されています。

wcstombsは、カウントが発生する前または発生したときにワイド文字のヌル文字(L'\ 0')に遭遇すると、それを8ビット0に変換して停止します。

これはドキュメントに明示的に記載されていませんが、入力文字列がnullで終了していない場合、出力文字列にバイトwcstombs()が書き込まれるまでワイド文字を読み取り続けると推測できます。countしたがって、nullで終了していないワイド文字列を処理している場合は、入力文字列の長さを知るだけでは不十分です。どういうわけか、出力文字列が必要なバイト数を正確に把握し(変換を行わずに決定することは不可能です)、それをcountパラメーターとして渡して、目的の処理をwcstombs()実行する必要があります。

なぜ私はこのnull終了の問題にそれほど焦点を合わせているのですか?MSDNFILE_NOTIFY_INFORMATION構造体のドキュメントには、そのFileNameフィールドについて次のように書かれているためです。

ディレクトリハンドルに関連するファイル名を含む可変長フィールド。ファイル名はUnicode文字形式であり、nullで終了していません。

フィールドがnullで終了していないという事実FileNameは、デバッガーでフィールドを見ると、フィールドの最後に「不明な漢字」がたくさんある理由を説明しています。構造のドキュメントには、この分野FILE_NOTIFY_INFORMATIONに関するもう1つの知恵も含まれています。FileNameLength

レコードのファイル名部分のサイズ(バイト単位)。

これは文字ではなくバイトを示していることに注意してください。したがって、入力文字列の各ワイド文字が出力文字列に正確に1バイトを生成すると想定したい場合でも、;を渡してはなりません。渡す必要があります(または、もちろん、nullで終了する入力文字列を使用します)。このすべての情報をまとめると、元の呼び出しが失敗した理由を最終的に理解できます。文字列の終わりを超えて読み取り、無効なデータをチョークしていました(これによりエラーがトリガーされました)。fileInfo.FileNameLengthcountfileInfo.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もコピーされるのはそのためです。

于 2011-12-24T22:44:42.563 に答える
0

私の推測では、渡した幅の広い文字列は無効であるか、正しく定義されていません。

どのようにpwFileName定義されていますか?FILE_NOTIFY_INFORMATIONとして定義された構造を持っているようですが、以下に示すように、fileInfoなぜを使用しないのですか?fileInfo.FileName

wcstombs(pfileName, fileInfo.FileName, fileInfo.FileNameLength);
于 2011-12-24T22:10:49.633 に答える
0

あなたが得るエラーはそれをすべて言います、それはそれがMBに変換できない文字を見つけました(それはMBで表現されていないので)、ソース

wcstombsがマルチバイト文字に変換できないワイド文字を検出した場合、タイプsize_tに-1キャストを返し、errnoをEILSEQに設定します。

このような場合、「想定される」入力を避け、失敗する実際のテストケースを提供する必要があります。

于 2011-12-24T22:45:20.233 に答える