如何在管道上使用WriteFile修复乱码文本?

时间:2009-03-15 12:21:36

标签: winapi named-pipes

我正在创建一个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时,字符串的结尾是乱码,我不知道为什么。有什么想法吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

对您发布的代码的一些观察:

  • 您需要1)显式发送空终止字节,或2)将1附加到您读取的数据。
  • 由于您正在读取512个字节,因此您还应该发送512个字节。
  • 您可以通过首先发送字符串的大小,然后发送那么多字节来发送可变长度字符串。这样,当您读取数据时,您将知道要为实际字符串读取多少字节。
  • 一旦你在管道上发送了两件东西,就会看到你所做的事情的问题,你在第一次阅读时就读到了你真正想要的东西。
  • 如果您只在管道上发送一件事,则可以保留代码,但在写入管道时发送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);

答案 1 :(得分:2)

我认为这里的关键是确保你的字符串以空值终止,并且你也发送终止字符。如果通信是同步的,或者您在PIPE_READMODE_MESSAGE中进行了设置,则不必发送整个缓冲区。当读取指定的字节数或在管道的另一端完成写入操作时,ReadFile将返回。我认为“乱码”文本在管道的客户端读取缓冲区中实际上是垃圾,并且因为您没有传输字符串终止字符,所以在发送到消息框的文本中包含它。要么在发送之前清除读取缓冲区,要么使用消息发送字符串终止字符,我认为它可以正常工作,而不会产生发送完整缓冲区的开销。

以下是来自MSDN的sample client。请注意客户端如何准确发送消息 + 1 (包括终止字符)中的字符数并接收到大小为512的固定缓冲区。如果您查看server example,我会看到相同的模式。

答案 2 :(得分:0)

我遇到了这个问题&#34;管道中的垃圾&#34;在编写泛型函数时,从命令提示符下执行的任何进程中读取stdout。因此,我无法改变写入管道的内容(通常建议),我只能改变读取方面。所以,我被欺骗了#34;。

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

我担心我可能会失去一个关键的最后一个字符(你可能会这样做!)但是出于我的直接目的,这并没有发生。您可以考虑在某些情况下添加null而不是替换结尾...

这里的代码snippit:

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';
}

更新:

经过进一步测试,我发现这不太可靠(令人震惊,是吧?)。我认为我是一个相对简单的解决方案,但我在正确的轨道上。让这个快速补丁工作的任何想法?