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) 显式发送空终止字节,或 2) 将一个附加到您读取的数据。
  • 由于您正在读取 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

在编写通用函数以从在命令提示符处执行的任何进程中读取标准输出时,我遇到了“管道中的垃圾”这个问题。因此,我无法更改写入管道的内容(正如通常建议的那样),我只能更改读取端。于是,我“作弊”了。

如果管道数据没有以空终止符结尾,我将最后一个字符替换为一个!它似乎对我有用。我完美地看到了这项工作,在我的数据块末尾有空值和没有空值的地方。

我担心我可能会丢失一个关键的最后一个字符(你可能会!),但就我的直接目的而言,这并没有发生。在某些情况下,您可能会考虑添加 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 回答