0

I have a Delphi application, which sends piece of text to a named pipe via call

SendMessageToNamedPipe(hPipe, CurMsg);

It works fine for some messages, but sending other texts leads to a crash of the application.

The only difference between normal and "crashing" messages I'm aware of is that the crashing messages contain lots of Cyrillic characters.

How should I encode them in order for the aforementioned call to be executed properly?

Update 1: Here's the implementation of 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;

Update 2: Version of the function, which seems to work.

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;
4

1 回答 1

6

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

但是 SendMessageToNamedPipe 既不是 Delphi 库函数也不是 Windows API 调用。我认为您需要查看 SendMessageToNamedPipe 正在做什么,因为几乎可以肯定这就是错误所在。您可能想问以下问题: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 在退出时修改 dwWrite 的值以给出写入字节数的计数。

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

正如大卫所写,在将 msg 写入管道之前,您实际上不需要将其复制到不同的缓冲区。表达式 PChar(msg) 返回指向以 null 结尾的 Chars 数组开头的指针,该数组包含 msg 中的数据。

反思您的代码,我会问您是否清楚管道另一端的程序是否期望接收 utf-16 字符串或 utf-8 字符串(甚至是 ANSI 字符串) )。您需要解决这个问题,然后相应地修改 SendMessageToNamedPipe。(此外,如果它需要一个以 null 结尾的字符串,而不是一个固定长度的缓冲区,您应该只发送预期的字节数,而不是 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 个字节。但碰巧的是,Delphi 的 Length 函数在应用于 Utf8String 或 RawByteString 时返回字节数,因此您可以编写 Length(utf8String)+1。+1 包含终止#0 字符,它不包含在长度计数中。(如果您传递的是 utf-16 字符串 Length 将返回字符数,因此需要乘以 SizeOf(Char))。

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

于 2012-11-01T14:34:54.560 回答