Question

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;
Était-ce utile?

La solution

Since the Windows pipe functions see the data written to the pipe as a binary stream it is not plausible that the type of data being written could cause a crash.

But SendMessageToNamedPipe is neither a Delphi library function nor a Windows API call. I think you need to look at what SendMessageToNamedPipe is doing, since that is almost certainly where the bug is. You might like to ask questions such as: what is the data type of CurMsg? How does SendMessageToNamedPipe calculate how many bytes to write to the pipe?

Update:

Reading through the implementation of SendMessageToNamedPipe that you've added to your question:

  1. sendBuf is OUT_BUF_SIZE+1 wide characters. You probably meant to define it as array[0..OUT_BUF_SIZE-1]. I see this mistake all the time. (But it is not the cause of the crash.)

  2. utf8String is assigned but never used.

  3. I think the cause of the crash is lstrcatw(sendBuf, PChar(msg)). It would crash if the length of the string passed in is greater than OUT_BUF_SIZE + 1 characters because this would overflow the buffer sendBuf.

  4. The test if not lpNumberOfBytesWritten is wrong. Or more to the point, the return value from WriteFile is a Boolean saying whether or not the write succeeded, not a count of the number of bytes written. WriteFile modifies the value of dwWrite on exit to give the count of the number of bytes written.

  5. Just spotted another one: WriteFile is sending OUT_BUF_SIZE bytes, but OUT_BUF_SIZE is the count of the number of characters in sendBuf, not the number of bytes. A Char in Delphi 2009 is 2 bytes (utf-16). (However good code would always use SizeOf(Char) rather than 2 because it could change in a future Delphi version, and has already changed once in the past.)

As David wrote, you don't actually need to copy msg to a different buffer before writing it to the pipe. The expression PChar(msg) returns a pointer to the start of the a null-terminated array of Chars that comprises the data in msg.

Reflecting on your code, I'd ask whether you are clear in your own mind whether the program at the other end of the pipe expects to receive a utf-16 string or a utf-8 string (or even an ANSI string for that matter). You need to settle this question and then modify SendMessageToNamedPipe accordingly. (Also, if it expects a null-terminated string, rather than a fixed-length buffer, you should send just the intended number of bytes, not OUT_BUF_SIZE bytes.)

Reply to your comment below:

  1. Your code above doesn't write utf-8 to the pipe. It writes utf-16. Although you called UTF8Encode you threw the result away, as I mentioned above.

  2. You can pass utf8String directly to WriteFile as the buffer to send by casting it like this: PRawByteString(utf8String). That expression returns a pointer to the first character in the utf8String just as for PChar(msg) which I explained above.

  3. You need to pass the correct number of bytes to write to WriteFile, instead of OUT_BUF_SIZE. Since it is a utf-8 string, a character can take up anything from 1 to 4 bytes. But as it happens, Delphi's Length function returns the number of bytes when applied to a Utf8String or a RawByteString so you can write Length(utf8String)+1. The +1 is to include the terminating #0 character, which is not included in the Length count. (If you were passing a utf-16 string Length would return the number of characters, so would need to be multiplied by SizeOf(Char)).

If you are still unclear, then you would probably benefit greatly from reading Delphi and Unicode.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top