在命名管道上发送文本会导致Delphi应用程序崩溃

时间:2012-11-01 14:19:45

标签: delphi delphi-2009 named-pipes

我有一个Delphi应用程序,它通过调用

将一段文本发送到命名管道
SendMessageToNamedPipe(hPipe, CurMsg);

它适用于某些消息,但发送其他文本会导致应用程序崩溃。

我所知道的正常和“崩溃”消息之间的唯一区别是崩溃消息包含大量的西里尔字符。

我应该如何对它们进行编码,以便正确执行上述调用?

更新1 :以下是SendMessageToNamedPipe的实现。

procedure SendMessageToNamedPipe(hPipe:THandle; msg:string);
  const
    OUT_BUF_SIZE = 100;
  var
    dwWrite : DWORD;
    lpNumberOfBytesWritten : LongBool;
    utf8String : RawByteString;
    sendBuf: array[0..OUT_BUF_SIZE] of WideChar;
  begin
    utf8String := UTF8Encode(msg);

    sendBuf[0] := #0;
    lstrcatw(sendBuf, PChar(msg));

    lpNumberOfBytesWritten := WriteFile(hPipe, sendBuf, OUT_BUF_SIZE, dwWrite, NIL);

    if not lpNumberOfBytesWritten then
    begin
      OutputDebugString(PChar('Sending error: ' + SysErrorMessage(GetLastError)));
    end
    else
    begin
      OutputDebugString(PChar('Message sent, dwWrite: ' + IntToStr(dwWrite)));
    end;
  end;

更新2 :该功能的版本,似乎有效。

procedure SendMessageToNamedPipe(hPipe:THandle; msg:string);
const
    OUT_BUF_SIZE = 200;
var
  dwWrite : DWORD;
  Success : LongBool;
  msgToSend : PChar;
  utf8String : RawByteString;
  sendBuf: array[0..OUT_BUF_SIZE-1] of WideChar;
  AnsiCharString : PAnsiChar;
begin
    OutputDebugString(PChar('SendMessageToNamedPipe.Length(msg): ' +     IntToStr(Length(msg))));
    OutputDebugString(PChar('Sending message: ' + msg));

    utf8String := UTF8Encode(msg);

    sendBuf[0] := #0;
    lstrcatw(sendBuf, PChar(msg));

    Success := WriteFile(hPipe, sendBuf, Length(sendbuf), dwWrite, NIL);

    if not Success then
    begin
      OutputDebugString(PChar('Sending error: ' + SysErrorMessage(GetLastError)));
    end
    else
    begin
      OutputDebugString(PChar('Message sent, dwWrite: ' + IntToStr(dwWrite)));
    end;
end;

1 个答案:

答案 0 :(得分:6)

由于Windows管道函数将作为二进制流写入管道的数据看作所写入的数据类型可能导致崩溃,这是不合理的。

但SendMessageToNamedPipe既不是Delphi库函数也不是Windows API调用。我想你需要看看SendMessageToNamedPipe正在做什么,因为这几乎肯定是bug的地方。您可能想问一些问题,例如:CurMsg的数据类型是什么? SendMessageToNamedPipe如何计算要写入管道的字节数?

<强>更新

阅读您已添加到问题中的SendMessageToNamedPipe的实现:

  1. sendBuf是OUT_BUF_SIZE + 1个宽字符。您可能想将其定义为数组[0..OUT_BUF_SIZE-1]。我总是看到这个错误。 (但这不是崩溃的原因。)

  2. 指定了utf8String但从未使用过。

  3. 我认为崩溃的原因是lstrcatw(sendBuf,PChar(msg))。如果传入的字符串的长度大于OUT_BUF_SIZE + 1个字符,它会崩溃,因为这会溢出缓冲区sendBuf。

  4. 如果不是lpNumberOfBytesWritten的测试是错误的。或者更重要的是,WriteFile的返回值是一个布尔值,表示写入是否成功,而不是写入的字节数。 WriteFile在exit上修改dwWrite的值,以给出写入的字节数。

  5. 刚发现另一个:WriteFile正在发送OUT_BUF_SIZE 字节,但OUT_BUF_SIZE是sendBuf中字符数的计数,而不是字节数。 Delphi 2009中的Char是2个字节(utf-16)。 (不过好的代码总是使用SizeOf(Char)而不是2,因为它可能会在未来的Delphi版本中发生变化,并且在过去已经改变了一次。)

  6. 正如David所写,在将其写入管道之前,实际上并不需要将msg复制到其他缓冲区。表达式PChar(msg)返回一个指向空终止数组Chars的开头的指针,该数组包含msg中的数据。

    反思您的代码,我会问您是否清楚自己是否清楚管道另一端的程序是否需要接收utf-16字符串或utf-8字符串(甚至是ANSI字符串)对于这个问题)。您需要解决此问题,然后相应地修改SendMessageToNamedPipe。 (另外,如果它需要一个以空字符结尾的字符串,而不是固定长度的缓冲区,则应该只发送预期的字节数,而不是OUT_BUF_SIZE字节。)

    回复下面的评论:

    1. 上面的代码不会将utf-8写入管道。它写了utf-16。虽然你调用了UTF8Encode,但你把结果扔掉了,如上所述。

    2. 你可以直接将utf8String传递给WriteFile作为要发送的缓冲区,方法如下:PRawByteString(utf8String)。该表达式返回指向utf8String中第一个字符的指针,就像我上面解释的PChar(msg)一样。

    3. 您需要传递正确的字节数才能写入WriteFile,而不是OUT_BUF_SIZE。由于它是一个utf-8字符串,因此一个字符可以占用1到4个字节。但实际上,当应用于Utf8String或RawByteString时,Delphi的Length函数返回 bytes 的数量,因此您可以编写Length(utf8String)+1。 +1将包括终止#0字符,该字符不包括在长度计数中。 (如果您传递的是utf-16字符串,则长度将返回字符数,因此需要乘以SizeOf(Char))。

    4. 如果您仍然不清楚,那么您可能会从阅读Delphi and Unicode中获益匪浅。