DP_*_*DP_ 0 delphi named-pipes delphi-2009
我有一个Delphi应用程序,它通过调用将一段文本发送到命名管道
SendMessageToNamedPipe(hPipe, CurMsg);
Run Code Online (Sandbox Code Playgroud)
它适用于某些消息,但发送其他文本会导致应用程序崩溃.
我所知道的正常和"崩溃"消息之间的唯一区别是崩溃消息包含大量的西里尔字符.
我应该如何对它们进行编码,以便正确执行上述调用?
更新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;
Run Code Online (Sandbox Code Playgroud)
更新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;
Run Code Online (Sandbox Code Playgroud)
由于Windows管道功能将写入管道的数据视为二进制流,因此写入的数据类型可能会导致崩溃,这是不合理的.
但SendMessageToNamedPipe既不是Delphi库函数也不是Windows API调用.我想你需要看看SendMessageToNamedPipe正在做什么,因为这几乎肯定是bug的地方.您可能想问一些问题,例如:CurMsg的数据类型是什么?SendMessageToNamedPipe如何计算要写入管道的字节数?
更新:
阅读已添加到问题中的SendMessageToNamedPipe的实现:
sendBuf是OUT_BUF_SIZE + 1个宽字符.您可能想将其定义为数组[0..OUT_BUF_SIZE-1].我总是看到这个错误.(但这不是崩溃的原因.)
utf8String已分配但从未使用过.
我认为崩溃的原因是lstrcatw(sendBuf,PChar(msg)).如果传入的字符串的长度大于OUT_BUF_SIZE + 1个字符,它会崩溃,因为这会溢出缓冲区sendBuf.
如果不是lpNumberOfBytesWritten的测试是错误的.或者更重要的是,WriteFile的返回值是一个布尔值,表示写入是否成功,而不是写入的字节数.WriteFile在exit上修改dwWrite的值,以给出写入的字节数.
刚发现另一个:WriteFile发送OUT_BUF_SIZE 字节,但OUT_BUF_SIZE是sendBuf中字符数的计数,而不是字节数.Delphi 2009中的Char是2个字节(utf-16).(但是好的代码总是使用SizeOf(Char)而不是2,因为它可能在将来的Delphi版本中发生变化,并且在过去已经改变了一次.)
正如David写的那样,在将msg写入管道之前,实际上并不需要将msg复制到不同的缓冲区.表达式PChar(msg)返回一个指针,该指针指向以空格终止的Chars数组的开头,该数组包含msg中的数据.
反思您的代码,我会问您是否清楚自己是否清楚管道另一端的程序是否需要接收utf-16字符串或utf-8字符串(或者甚至是ANSI字符串) ).您需要解决此问题,然后相应地修改SendMessageToNamedPipe.(另外,如果它需要一个以空字符结尾的字符串,而不是固定长度的缓冲区,则应该只发送预期的字节数,而不是OUT_BUF_SIZE字节.)
回复下面的评论:
上面的代码不会将utf-8写入管道.它写了utf-16.虽然你调用了UTF8Encode,但是你把结果扔掉了,正如我上面提到的那样.
您可以直接将utf8String传递给WriteFile作为要发送的缓冲区,如下所示:PRawByteString(utf8String).该表达式返回指向utf8String中第一个字符的指针,就像我上面解释的PChar(msg)一样.
您需要传递正确的字节数以写入WriteFile,而不是OUT_BUF_SIZE.由于它是一个utf-8字符串,因此一个字符可以占用1到4个字节.但实际上,Delphi的Length函数返回应用于Utf8String或RawByteString时的字节数,因此您可以编写Length(utf8String)+1.+1将包括终止#0字符,该字符不包括在长度计数中.(如果传递的是utf-16字符串,则长度将返回字符数,因此需要乘以SizeOf(Char)).
如果您仍然不清楚,那么您可能会从阅读Delphi和Unicode中获益匪浅.