Sending text over a named pipe crashes Delphi application - delphi

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;

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:
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.)
utf8String is assigned but never used.
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.
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.
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:
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.
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.
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.

Related

Does Delphi handle automatic type conversion to/from AnsiString and ShortString? [duplicate]

I want to be able to use a string that is quite long (not longer then 100000 signs).
As far as I know a typical string variable can cotain only up to 256 chars.
Is there a way to store such a long string?
Old-style (Turbo Pascal, or Delphi 1) strings, now known as ShortString, are limited to 255 characters (byte 0 was reserved for the string length). This appears to still be the default in FreePascal (according to #MarcovandeVoort's comment below). Keep reading, though, until you get to the discussion and code sample for AnsiString below. :-)
Currently, most other dialects of Pascal I'm aware of default to either AnsiString (long strings of single byte characters) or UnicodeString (long strings of multi-byte characters). Neither of those are limited to 255 characters.
The current versions of Delphi defaults to UnicodeString as the default type, so declaring a string variable is in fact a long UnicodeString. There is no practical upper limit to the string length:
var
Test: string; // Declare a new Unicode string
begin
SetLength(Test, 100000); // Initialize it to hold 100000 characters
Test := StringOfChar('X', 100000); // Fill it with 100000 'X' characters
end;
If you want to force single-byte characters (but not be limited to 255 character strings), use AnsiString (which can set as the default string type in FreePascal if you use the {$H+} compiler directive - thanks #MarcovandeVoort):
var
Test: AnsiString; // Declare a new Ansistring
begin
SetLength(Test, 100000); // Initialize it to hold 100000 characters
Test := StringOfChar('X', 100000); // Fill it with 100000 'X' characters
end;
Finally, if you do for some unknown reason want to use the old style ShortString that is restricted to 255 characters, declare it as such, either using ShortString or the old style String[Size] declaration:
var
Test: ShortString; // Declare a new short string of 255 characters
ShortTest: String[100]; // Also a ShortString of 100 characters
begin
// This line won't compile, because it's too large for Test
Test := StringOfChar('X', 100000); // Fill it with 100000 'X' characters
end;
In Free Pascal, you do not need to be worry about this. You only need to insert the directive {$H+} at the beginning of the source code.
{$H+}
var s: String;
begin
s := StringOfChar('X', 1000);
writeln(s);
end.
You can use the AnsiString type.

Delphi - SysUtils.Trim not deleting last space(?) char

Delphi RIO. I have built an Excel PlugIn with Delphi (also using AddIn Express). I iterate through a column to read cell values. After I read the cell value, I do a TRIM function. The TRIM is not deleting the last space. Code Snippet...
acctName := Trim(UpperCase(Acctname));
Before the code, AcctName is 'ABC Holdings '. It is the same AFTER the TRIM function. It appears that Excel has added some type of other char there. (new line?? Carriage return??) What is the best way to get rid of this? Is there a way I can ask the debugger to show me the HEX value for this variable. I have tried the INSPECT and EVALUATE windows. They both just show text. Note that I have to be careful of just deleting NonText characters, and some companies names have dashes, commas, apostrophes, etc.
**Additional Info - Based on Andreas suggestion, I added the following...
ShowMessage(IntToHex(Ord(Acctname[Acctname.Length])));
This comes back with '00A0'. So I am thinking I can just do a simple StringReplace... so I add this BEFORE Andreas code...
acctName := StringReplace(acctName, #13, '', [rfReplaceAll]);
acctName := StringReplace(acctName, #10, '', [rfReplaceAll]);
Yet, it appears that nothing has changed. The ShowMessage still shows '00A0' as the last character. Why isn't the StringReplace removing this?
If you want to know the true identity of the last character of your string, you can display its Unicode codepoint:
ShowMessage(IntToHex(Ord(Acctname[Acctname.Length]))).
Or, you can use a utility to investigate the Unicode character on the clipboard, like my own.
Yes, the character in question is U+00A0: NO-BREAK SPACE.
This is like a usual space, but it tells the rendering application not to put a line break at this space. For instance, in Swedish, at least, you want non-breaking spaces in 5 000 kWh.
By default, Trim and TStringHelper.Trim do not remove this kind of whitespace. (They also leave U+2007: FIGURE SPACE and a few other kinds of whitespace.)
The string helper method has an overload which lets you specify the characters to trim. You can use this to include U+00A0:
S.Trim([#$20, #$A0, #$9, #$D, #$A]) // space, nbsp, tab, CR, LF
// (many whitespace characters missing!)
But perhaps an even better solution is to rely on the Unicode characterisation and do
function RealTrimRight(const S: string): string;
var
i: Integer;
begin
i := S.Length;
while (i > 0) and S[i].IsWhiteSpace do
Dec(i);
Result := Copy(S, 1, i);
end;
Of course, you can implement similar RealTrimLeft and RealTrim functions.
And of course there are many ways to see the actual string bytes in the debugger. In addition to writing things like Ord(S[S.Length]) in the Evaluate/Modify window (Ctrl+F7), my personal favourite method is to use the Memory window (Ctrl+Alt+E). When this has focus, you can press Ctrl+G and type S[1] to see the actual bytes:
Here you see the string test string. Since strings are Unicode (UTF-16) since Delphi 2009, each character occupies two bytes. For simple ASCII characters, this means that every second byte is null. The ASCII values for our string are 74 65 73 74 20 73 74 72 69 6E 67. You can also see, on the line above (02A0855C) that our string object has reference count 1 and length B (=11).
As a demo, to show the unicode string:
program q63847533;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
array100 = array[0..99] of Byte;
parray100 = ^array100;
var
searchResult : TSearchRec;
Name : string;
display : parray100 absolute Name;
dummy : string;
begin
if findfirst('z*.mp3', faAnyFile, searchResult) = 0 then
begin
repeat
writeln('File name = '+searchResult.Name);
name := searchResult.Name;
writeln('File size = '+IntToStr(searchResult.Size));
until FindNext(searchResult) <> 0;
// Must free up resources used by these successful finds
FindClose(searchResult);
end;
readln(dummy);
end.
My directory contains two z*.mp3 files, one with an ANSI name and the other with a Unicode name.
WATCHing display^ as Hex or Memorydump will display what you seem to require (the Is there a way I can ask the debugger to show me the HEX value for this variable. of your question)

Error Delphi XE2 - Exception class $C00000005

I am getting this error will debugging a project, which used to be in Delphi 7 and I have been upgrading to Delphi XE2, the same error happens in several methods.
First chance exception at $006DC660. Exception class $C0000005 with message 'access violation at 0x006dc660 read of address 0xffffffff'
This is one of the methods:
PFI = ^TFI;
TFI = record
Id : TToken;
Name : TName;
Parameters : string;
end;
function TListFI.IsIn(S: PChar): PFI;
function SearchName2(Item: PFI):Boolean;
var N1, N2: PChar;
begin
N1:= StrNew(Item^.Name);
N2:= StrNew(S); //Here is the issue
SearchName2:= (StrComp(StrUpper(N1), StrUpper(N2)) = 0);
StrDispose(N1);
StrDispose(N2);
end;
begin
IsIn:= PFI(FirstThat(#SearchName2));
end;
I have googled and I found someone describing a similar problem, and he affirms that when the incremental linker is disabled it works, can someone tell me what and where is it or give some advice to solve this situation.
[EDIT]
Removing the # now gives me the following error in IsIn:= PFI(FirstThat(SearchName2));
E2010 Incompatible types: 'TObject' and 'PFI'
I am adding the FirstThat procedure to see if it may help.
TFuncionColeccion = function (Elemento: TObject): Boolean;
function TColeccion.FirstThat (Rutina: TFuncionColeccion): TObject;
var
i: Integer;
begin
For i:=0 to Count-1 do
if Rutina(Items[i]) then
begin
FirstThat:=Items[i];
exit;
end;
FirstThat:=nil;
end;
It is (and always has been) an error to call local (nested) procedures by pointer, which is clearly what your FirstThat function does. The compiler has to do special things with the stack to call local functions and give them access to the parent scope's variables (S in your code), but the compiler can only know to do those special things when the local function is called directly. The compiler cannot know that the argument to FirstThat will be a local function, so it doesn't include the special code when FirstThat invokes the pointed-to function.
The bottom line is that the stack inside the function doesn't get set up the way it's supposed to, and that means any number of strange symptoms may appear. You'll have to use some other way. Maybe make SearchName2 be a two-argument function, and then write FirstThat to accept S as a parameter that it can forward to the function argument.
You shouldn't need to use the # operator when constructing a function pointer. When you do, the compiler tends to skip type checking, which is what allowed you to pass a local function pointer to FirstThat in the first place. When the function you're passing really matches the required prototype, the compiler will allow you to pass it without the # operator.
You are reporting an access violation in
StrNew(S)
where S is of type PChar. The explanation for that, with probability very close to 1, is that S is not in fact a pointer to null terminated array of WideChar.
In Delphi 7, PChar is an alias for PAnsiChar. That is a pointer to null terminated array of AnsiChar, i.e. 8 bit characters. In Delphi XE2, PChar is an alias for PWideChar, a pointer to null terminated array of WideChar, i.e. 16 bit characters.
It helps to understand what StrNew does. It walks the array until it finds a null character. For 8 bit text that is a single zero byte. For 16 bit text, the null is a zero 16 bit word. Then it allocates a new block of memory of the same length as the input string, and makes a copy into that new memory. The source code is:
function StrNew(const Str: PWideChar): PWideChar;
var
Size: Cardinal;
begin
if Str = nil then Result := nil else
begin
Size := StrLen(Str) + 1;
Result := StrMove(WideStrAlloc(Size), Str, Size);
end;
end;
The only plausible failure mode is that when StrLen walks the array, it attempts an invalid memory read. And that can only happen if your input parameter is invalid. In other words, this must be a programming error on your part.
One possible explanation is that you are in fact passing 8 bit text to this function despite promising to pass 16 bit text. An easy mistake to make, especially if you are not yet fully familiar with the Unicode change. The 8 bit text has a zero terminator, but the byte that follows happens not to be zero. Or the zero byte falls at an odd numbered offset from the start. And then StrNew continues walking the buffer, but now it is off the end and it so happens that it doesn't find a zero word before overrunning into an address that has not been allocated. And that is an access violation.
If that is so then solution will be either:
Change the function's parameter to be of type PAnsiChar, and fix the dubious casting at the call site.
Pass the function 16 bit text as it requires.
In your update you include the address which cannot be read, 0xffffffff. This is -1 in hex. And that would seem to be the most prosaic of errors. Your pointer is completely bogus! Your exact error message can be reproduced with this code: StrNew(PChar(-1)).
I don't have enough information here to tell you why your pointer is bogus. Hopefully you've learnt some debugging and diagnostic techniques that will enable you to solve the problem. At least you now know that the error is in your code.
Assuming that BuscaName2 and SearchName2 are one and the same thing, then you need look no further. Local procedures can only be called from a containing function. As #Rob correctly says, the use of # with procedures is almost always incorrect and is a warning sign of serious problems with your code.

Undocumented Members of TPropInfo

System.TypInfo.TPropInfo has two function members (at least in D-XE3):
function NameFld: TTypeInfoFieldAccessor; inline;
function Tail: PPropInfo; inline;
I cannot find any documentation for them or any examples of their use. What are they for and how can they be used? (Hope that qualifies as one question.)
The NameFld function returns the name of a property as a TTypeInfoFieldAccessor.
This allows you to do the following:
MyPropertyName:= MyPropInfo.NameFld.ToString;
if (PropInfoA.NameFld = PropInfoB.NameFld) then begin
writeln('property names are the same');
end;
The TTypeInfoFieldAccessor stores the name of a property in a shortstring internally.
Because the NextGen compiler does not support shortstrings, a PByte type is used.
(I guess the author did not want to litter the source with ifdefs and ripped out the PShortstring references)
The input of Tail is a PByte pointing to length field of the internal shortstring.
Here's the source code for tail.
function TTypeInfoFieldAccessor.Tail: PByte;
begin
Result:=
FData //Start of the shortstring
+ FData^ + //Length of the stringData
+ 1; //Add one for the length byte itself
end;
Because shortstrings are not null terminated, you cannot do a simple "loop until the null char is found" kind of loop.
Therefore a loop from start to tail can employed to transfer the shortstring into a normal string.
Strangely enough in the actual RTL sourcecode the length byte is used everywhere instead of the tail function; so it looks like a leftover.
It would have made more sense to include a size function and rip out the tail.

Indy 9 read data in hexadecimal format

I need a method for reading data from TCP connection in hex format instead string format, to explain more lets give this example :
When we sent data like "Test" over TCP connection, each caracter is encoded with is ASCII code " 54 65 73 54 ", at the other side I use currentReadbuffer to read data, my need is to get data in ASCII hex format without converting to string values.
you can convert each character-pair from hex to byte by using something like this:
function HexToByte(const value: string): Byte;
begin
Result := StrToInt('$'+Value);
end;
depending on the Delphi version you use and character encoding you use (is it single-byte, multi-byte?) you need to convert these Bytes to characters.
Assuming you use single-byte character sets (ASCII, Windows-1252 or the like), and Delphi < 2009, the conversion is easy:
function ByteToChar(const value: Byte): Char;
begin
Result := Char(Byte);
end;
Edit your question to make it more specific, and you get a more focussed answer.
--jeroen

Resources