4

私が作成している Win32 アプリケーションがあり、名前付きパイプを介してあるプロセスから別のプロセスに文字列を送信します。ただし、パイプで ReadFile を呼び出すプロセスは、文字化けしたデータを含む文字列を取得します。正しく書き込まれたバイト数を返しますが、文字列の最後の 8 文字程度が文字化けしています。

パイプを作成して書き込むコードは次のとおりです。

myPipe = CreateNamedPipe(L"\\\\.\\pipe\\testpipe", PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT, 10, 512, 512, 10, NULL);
TCHAR title[128];
GetWindowText(foundHwnd, title, 128);
wstring windowTitle(title);
vector<wstring> splitVec;
boost::split(splitVec, windowTitle, boost::algorithm::is_any_of(wstring(L"|")));
WriteFile(myPipe, splitVec[0].c_str(), splitVec[0].size(), &wrote, NULL);

そして、これを読み取るコードは次のとおりです。

if (WaitNamedPipe(L"\\\\.\\pipe\\testpipe", 5000) == 0) {
    MessageBox(NULL, L"Unable to wait for pipe", L"Error", MB_OK);
    return false;
}

myPipe = CreateFile(L"\\\\.\\pipe\\testpipe", GENERIC_READ, FILE_SHARE_READ, NULL,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (myPipe == INVALID_HANDLE_VALUE) {
    MessageBox(NULL, L"Unable to open pipe", L"Error", MB_OK);
    return false;
}
    // Other code here...
    TCHAR buf[512];
    DWORD read;
    success = ReadFile(myPipe, buf, 512, &read, NULL);
    if (read > 0)
        MessageBox(NULL, buf, L"Got Data", MB_OK);

MessageBox が表示されると、文字列の末尾が文字化けしてしまい、その理由がわかりません。何か案は?

ありがとう!

4

3 に答える 3

3

ここで重要なのは、文字列がヌルで終了していることと、終了文字も送信していることを確認することだと思います。通信が同期の場合、または で設定した場合は、バッファ全体を送信する必要はありませんPIPE_READMODE_MESSAGE。指定されたバイト数が読み取られるか、パイプの反対側で書き込み操作が完了すると、ReadFile が返されます。「文字化けした」テキストは、パイプのクライアント側の読み取りバッファーで実際にはゴミであり、文字列終了文字を送信していないため、メッセージ ボックスに送信されるテキストにこれが含まれていると思います。送信する前に読み取りバッファをクリアするか、メッセージとともに文字列終了文字を送信すると、完全なバッファを送信するオーバーヘッドなしで機能すると思います。

MSDN のサンプル クライアントを次に示します。クライアントがメッセージ内の正確な文字数+ 1 (終了文字を含む) を送信し、サイズ 512 の固定バッファーに受信する方法に注意してください。サーバーの例を見ると、同じパターンが表示されます。

于 2009-03-15T13:13:20.720 に答える
2

あなたが投稿したコードに関するいくつかの観察:

  • 1) null 終端バイトを明示的に送信するか、2) 読み取ったデータに 1 を追加する必要があります。
  • 512 バイトを読み取っているので、正確に 512 バイトも送信する必要があります。
  • 最初に文字列のサイズを送信し、次にそのバイト数を送信することで、代わりに可変長文字列を送信できます。そうすれば、データを読み取るときに、実際の文字列を読み取るバイト数がわかります。
  • あなたがしたことの問題は、パイプを介して2つのものを送信するとすぐに表示され、最初の読み取りで本当に必要なものを読み過ぎます。
  • パイプを介して 1 つだけ送信する場合は、コードを保持できますが、パイプに書き込むときに size() + 1 を送信します。
  • ReadFile / WriteFile は、必ずしも文字列ではなく、バイナリ データを送信するためのものでした。したがって、最初にサイズを読み書きしてから実際の文字列を読み書きするという私の提案を実装する ReadString および WriteString という関数を作成できます。

次のようなことを試してください:

パイプを作成して書き込むコードは次のとおりです。

myPipe = CreateNamedPipe(L"\\\\.\\pipe\\testpipe", PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT, 10, 512, 512, 10, NULL);
TCHAR title[128];
GetWindowText(foundHwnd, title, 128);
WriteFile(myPipe, title, 128*sizeof(TCHAR), &wrote, NULL);//<---In this case we are sending a null terminated string buffer.

そして、これを読み取るコードは次のとおりです。

if (WaitNamedPipe(L"\\\\.\\pipe\\testpipe", 5000) == 0) {
    MessageBox(NULL, L"Unable to wait for pipe", L"Error", MB_OK);
    return false;
}

myPipe = CreateFile(L"\\\\.\\pipe\\testpipe", GENERIC_READ, FILE_SHARE_READ, NULL,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (myPipe == INVALID_HANDLE_VALUE) {
    MessageBox(NULL, L"Unable to open pipe", L"Error", MB_OK);
    return false;
}
    // Other code here...
    TCHAR buf[128];
    DWORD read;
    success = ReadFile(myPipe, buf, 128*sizeof(TCHAR), &read, NULL);
    if (read > 0)
        MessageBox(NULL, buf, L"Got Data", MB_OK);
于 2009-03-15T12:26:34.937 に答える
0

コマンドプロンプトで実行されたプロセスからstdoutを読み取る汎用関数を作成しているときに、「パイプ内のガベージ」でこの問題に遭遇しました。したがって、パイプに書き込まれている内容を変更することはできませんでした (よく言われているように)。変更できるのは読み取り側だけでした。だから「だまされた」。

パイプ データがヌル ターミネータで終わらない場合は、最後の文字を 1 に置き換えました。それは私にとってはうまくいくようでした。これは、データ チャンクの最後に null がある場所とない場所で完全に機能することがわかりました。

私は重要な最後の文字を失うのではないかと心配しました (そして、あなたがそうする可能性があります!) が、当面の目的のために、それは起こりませんでした. 状況によっては、末尾を置き換えるのではなく、null を追加することを検討することもできます...

コードスニピットは次のとおりです。

const unsigned int MAX_PIPE_PEEKS = 100;
DWORD bytesInPipe = 0;
unsigned int pipePeeks=0;
while( (bytesInPipe==0) && (pipePeeks < MAX_PIPE_PEEKS) )
{
    bSuccess = PeekNamedPipe( g_hChildStd_OUT_Rd, NULL, 0, NULL, 
                              &bytesInPipe, NULL );
    if( !bSuccess ) return bSuccess;  // Bail on critical failure                
    ++pipePeeks;
}
if( bytesInPipe > 0 )
{
    // Read the data written to the pipe (and implicitly clear it)
    DWORD dwRead; 
    CHAR *pipeContents = new CHAR[ bytesInPipe ];    
    bSuccess = ReadFile( g_hChildStd_OUT_Rd, pipeContents, 
                         bytesInPipe, &dwRead, NULL );
    if( !bSuccess || dwRead == 0  )  return FALSE;  // Bail on critical failure              

    // "Cheat" - eliminate garbage at the end of the pipe
    if( pipeContents[ bytesInPipe ] != '\0' )
        pipeContents[ bytesInPipe ] = '\0';
}

アップデート:

さらにテストした結果、これはあまり信頼できないことがわかりました (ショッキングですね?)。比較的単純な解決策ですが、私は正しい軌道に乗っていると思います。このクイック パッチを機能させるためのアイデアはありますか?

于 2015-07-01T14:38:12.217 に答える