Как исправить искаженный текст с помощью WriteFile на трубе? - PullRequest
4 голосов
/ 15 марта 2009

У меня есть приложение 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 ]

2 голосов
/ 15 марта 2009

Я думаю, что ключ здесь в том, чтобы убедиться, что ваши строки имеют нулевое завершение и что вы также отправляете символ завершения. Вам не нужно отправлять весь буфер, если связь является синхронной или если вы установили его в PIPE_READMODE_MESSAGE. ReadFile вернется, когда будет прочитано указанное количество байтов или операция записи завершится на другом конце канала. Я считаю, что «искаженный» текст на самом деле является мусором в буфере чтения на клиентской стороне канала, и поскольку вы не передаете символ завершения строки, он включается в текст, отправляемый в окно сообщения. Либо очистите буфер чтения перед отправкой, либо отправьте символ завершения строки вместе с сообщением, и я думаю, что он будет работать без дополнительных затрат при отправке полного буфера.

Вот образец клиента из MSDN. Обратите внимание, как клиент отправляет ровно количество символов в сообщении + 1 (включая символ завершения) и получает в фиксированный буфер размером 512. Если вы посмотрите на пример сервера, вы увидите ту же картину.

2 голосов
/ 15 марта 2009

Некоторые замечания по коду, который вы разместили:

  • Вам необходимо либо 1) явным образом отправить завершенный нулевой байт, либо 2) добавить один к прочитанным данным.
  • Поскольку вы читаете 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);
0 голосов
/ 01 июля 2015

Я столкнулся с этой проблемой с «мусором в канале» при написании универсальной функции для чтения стандартного вывода из любого процесса, выполняемого в командной строке. Поэтому я не мог изменить то, что записывалось в канал (как это обычно предлагается), я мог только изменить сторону чтения. Итак, я "обманул".

Если данные канала не заканчивались нулевым терминатором, я заменил последний символ одним! Казалось, это работает для меня. Я прекрасно видел эту работу там, где были нули и где не было в конце моих кусков данных.

Я беспокоился, что могу потерять последний критический символ (и возможно, что вы могли бы!), Но для моих непосредственных целей этого не произошло. Вы можете добавить ноль вместо замены конца при некоторых обстоятельствах ...

Вот фрагмент кода:

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

UPDATE:

После дальнейших испытаний я обнаружил, что это не совсем надежно (шокирует, да?). Я думаю, что я на правильном пути, хотя для относительно простого решения. Есть идеи, как заставить этот быстрый патч работать?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...