Undocumented Members of TPropInfo - delphi

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.

Related

How can I receive a string from FPC DLL? [duplicate]

This question already has answers here:
Need simple demo call Delphi DLL in C++
(2 answers)
Closed 8 years ago.
How can I receive a string from a FPC DLL? I would like to send two pointers concat them and receive the result in another string in Delphi.
library Test;
{$mode Delphi}
uses
Classes;
function Concat(const S1, S2: PWideChar): String; cdecl;
begin
Result := S1 + S2;
end;
exports
Concat name 'Concat';
begin
end.
In Delphi, a String is a complex, structured type with many details managed for you by the compiler and RTL 'magic' that hides these details. In particular, for 'long strings' there is a reference count and a length and, depending on the Delphi version involved, possibly other information.
Any DLL cannot know the details of precisely what information is required to be returned (or may be present in) any 'string' variables (or results) that an application may require. The DLL may not even be called by a Delphi program at all, in which case the 'string' type will be quite different again.
For this reason, a DLL will usually choose to deal with strings as simple 'C'-style pointer to char types. That is, some pointer to a null terminated region of memory. The caller of the DLL must then also ensure to exchange 'string' values with the DLL accordingly.
In the case of some function returning a value, the issue is complicated by the fact that allocation of the area of memory required to hold the result must be performed by the caller, with the function in the DLL taking appropriate steps to ensure that the memory supplied is sufficient. Applying these principles in this case results in a DLL routine that might look similar to this:
function Concat(const S1, S2, DEST: PWideChar; const aMaxLen: Integer): Boolean; cdecl;
begin
// left as exercise
end;
This is a simple implementation that returns TRUE if aMaxLen is sufficient to accommodate the concatenated result. You should also consider other behaviours of the function under a variety of conditions (eg. S1 or S2 or both are NIL, aMaxLen is too big, etc).
Whatever implementation choices are made for performing the concatenation (left as an exercise for you), the result of the function call must be to place the result in the buffer pointed to by DEST.
The caller must then also ensure that a buffer of sufficient length is provided and the correct length indicated in the call:
var
a, b, ab: WideString; // Or: String or UnicodeString in Delphi 2009 and later
begin
a := 'foo';
b := 'bar';
// Make sure 'ab' is big enough to hold the concatenated result of a + b
SetLength(ab, Length(a) + Length(b));
if Concat(PWideChar(a), PWideChar(b), PWideChar(ab), Length(ab)) then
// success: ab == 'foobar'
else
// something went wrong
end;
The question has to be asked though: Why are you doing this in an FPC DLL when Delphi already handles the concatenation of strings quite comfortably ? O.o

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.

Sending text over a named pipe crashes Delphi application

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.

Delphi compiler error E2064 left side cannot be assigned to

I inherited a Delphi application and I know nothing about object pascal.
It's a BPL that I need to compile into the new version of C++ Builder XE.
When I run a make I get the error:
E2064 left side cannot be assigned to.
I've learned enough obj pascal to know I have a constant that is trying to be assigned a value.
But, apparently, you can over ride this behanvior; essentially turning constants into vars by going into Build options under the Delphi compiler and turning on "Assignable Typed constants".
I did that and I continue to get the same error.
I tried surrounding my code with {$J+} and {$J-} and still it will not compile.
procedure TChunkIDAT.CopyInterlacedRGB8(const Pass: Byte;
Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar );
var
Col: Integer;
begin
{Get first column and enter in loop}
Col := ColumnStart[Pass];
Dest := pChar(Longint(Dest) + Col * 3);
repeat
{Copy this row}
Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
Get the error on last line. If I change the const to a var, I then get the error that the declaration differs from the previous declaration but I have no idea where the previous declaration is....
You're type-casting a two-byte thing (Char) into a one-byte thing (Byte). Reading that value is easy to define, but making that value writable is tricky, probably for the same reason the types of formal and actual "var" parameters need to be identical.
Maybe you wanted to type-cast it to a two-byte thing, such as Word. Or maybe you want GammaTable to be an array of Char so you don't have to type-cast at all. Or maybe, if this code was originally written for a Delphi version earlier than 2009, you want those PChar declarations to be PAnsiChar — character types have gotten wider. Another option is to type-cast Dest to PByte, and then dereference the result. That's probably a bad idea, though, because you'll only be overwriting every other byte of the buffer.
Based on the name of the function, it sounds like PChar was never the right data type to use. That type is for character data, but I think this code is dealing with bytes. The correct thing to do is probably to change PChar to PByte, and then you don't need to type-cast Dest at all.
The $J directive is irrelevant; it controls whether the compiler will allow you to assign values to typed constants. You don't have any of those in this code.
The reason is that as of Delphi 2009, Char, PChar, and String are Unicode, and store more than one byte per character.
You should not cast those pointers to bytes, and the compiler prevents you from assigning them if you cast the left side of an assignment to a byte.
This compiles:
procedure CopyInterlacedRGB8(const Pass: Byte; Dest: pAnsiChar); overload;
begin
Byte(Dest^) := Pass;
end;
This doesn't:
procedure CopyInterlacedRGB8(const Pass: Byte; Dest: pChar); overload;
begin
Byte(Dest^) := Pass;
end;
Instead of pChar, you should use pByte, which makes the code simpler:
procedure CopyInterlacedRGB8(const Pass: Byte; Dest: PByte); overload;
begin
Dest^ := Pass;
end;
--jeroen
That looks like you're working with Gustavo Daud's TPngImage library. You don't need that code in an external BPL because it's been included in the RTL since D2009. Remove that unit from the BPL and you should be able to get at the updated version via the PngImage unit.

Delphi: Fast(er) widestring concatenation

i have a function who's job is to convert an ADO Recordset into html:
class function RecordsetToHtml(const rs: _Recordset): WideString;
And the guts of the function involves a lot of wide string concatenation:
while not rs.EOF do
begin
Result := Result+CRLF+
'<TR>';
for i := 0 to rs.Fields.Count-1 do
Result := Result+'<TD>'+VarAsWideString(rs.Fields[i].Value)+'</TD>';
Result := Result+'</TR>';
rs.MoveNext;
end;
With a few thousand results, the function takes, what any user would feel, is too long to run. The Delphi Sampling Profiler shows that 99.3% of the time is spent in widestring concatenation (#WStrCatN and #WstrCat).
Can anyone think of a way to improve widestring concatenation? i don't think Delphi 5 has any kind of string builder. And Format doesn't support Unicode.
And to make sure nobody tries to weasel out: pretend you are implementing the interface:
IRecordsetToHtml = interface(IUnknown)
function RecordsetToHtml(const rs: _Recordset): WideString;
end;
Update One
I thought of using an IXMLDOMDocument, to build up the HTML as xml. But then i realized that the final HTML would be xhtml and not html - a subtle, but important, difference.
Update Two
Microsoft knowledge base article: How To Improve String Concatenation Performance
WideString are inherently slow because they were implemented for COM compatibility and go through COM calls. If you look at the code, it will keep on reallocating the string and call SysAllocStringLen() & C which are APIs from oleaut32.dll. It doesn't use the Delphi memory manager but AFAIK it uses the COM memory manager.
Because most HTML pages don't use UTF-16, you may get better result using the native Delphi string type and a string list, although you should be careful about conversion from UTF and the actual codepage, and the conversion will downgrade performance as well.
Also you're using a VarAsString() function that probably converts a variant to an AnsiString then converted to a WideString. Check if your version of Delphi has a VarAsWideString() or something alike function to avoid it, or rely on Delphi automatic conversion if you could be sure your variant will never be NULL.
Yup, your algorithm is clearly in O(n^2).
Instead of returning a string, try returning a TStringList, and replace your loop with
while not rs.EOF do
begin
Result.Add('<TR>');
for i := 0 to rs.Fields.Count-1 do
Result.Add( '<TD>'+VarAsString(rs.Fields[i].Value)+'</TD>' );
Result := Result.Add('</TR>');
rs.MoveNext;
end;
You can then save your Result using TStringList.SaveToFile
I'm unable to spend the time right now to give you the exact code.
But I think the fastest thing you can do is:
Loop through all the strings and total their length also adding for the extra table tags you'll need.
Use SetString to allocate one string of the proper length.
Loop through all the strings again and use the "Move" procedure to copy to the string to the proper place in the final string.
The key thing is that many concatenations to a string take longer and longer because of the constant allocating and freeing of memory. A single allocation will be your biggest timesaver.
i found the best solution. The open source HtmlParser for Delphi, has a helper TStringBuilder class. It is internally used to build what he calls DomStrings, which is actually an alias of WideString:
TDomString = WideString;
With a little bit of fiddling of his class:
TStringBuilder = class
public
constructor Create(ACapacity: Integer);
function EndWithWhiteSpace: Boolean;
function TailMatch(const Tail: WideString): Boolean;
function ToString: WideString;
procedure AppendText(const TextStr: WideString);
procedure Append(const value: WideString);
procedure AppendLine(const value: WideString);
property Length: Integer read FLength;
end;
The guts of the routine becomes:
while not rs.EOF do
begin
sb.Append('<TR>');
for i := 0 to rs.Fields.Count-1 do
sb.Append('<TD>'+VarAsWideString(rs.Fields[i].Value));
sb.AppendLine('</TR>');
rs.MoveNext;
end;
The code then feels to run infinitely afaster. Profiling shows much improvement; the WideString manipulation and length-counting became negligible. In its place was FastMM's own internal operations.
Notes
Nice catch on the mistaken forcing of all strings into current code-page (VarAsString rather than VarAsWideString)
Some HTML closing tags are optional; omitted ones that logically make no sense.
Widestring is not reference counted, any modification means a string manipulation. If your content is not unicode encoded, you can internally use the native string (reference counted) to concatenate string and then convert it to a Widestring. Example is as follows:
var
NativeString: string;
begin
// ...
NativeString := '';
while not rs.EOF do
begin
NativeString := NativeString + CRLF + '<TR>';
for i := 0 to rs.Fields.Count-1 do
NativeString := NativeString + '<TD>'+VarAsString(rs.Fields[i].Value) + '</TD>';
NativeString := NativeString + '</TR>';
rs.MoveNext;
end;
Result := WideString(NativeString);
I have also seen another approach: Encode Unicode to UTF8String (as reference counted), concatenate them and finally convert UTF8String to Widestring. But I am not sure, if two UTF8String can be concatenated directly. The time on encoding should also be considered.
Anyway, although Widestring concatenation is much slower than native string operations. But it is IMO still acceptable. Too much tuning on such kind of thing should be avoided. Seriously considering of performance, you should then upgrade your Delphi to at least 2009. The costs on buying a tool is for long-term cheaper than doing heavy hacks on an old Delphi.

Resources