StrtoInt64Def convert error in delphi - delphi

I am having small program having ConvertFunction which converts the string data to other formats. The return type of function is Variant. When i use StrToIntDef to convert integer value then it does without any error.
But now my number is very large so I want to use int64 instead of integer. When i use StrToInt64def function to convert its giving me error, Incompatiable types:Variant and int64.
Below are the both codes. Any idea whats wrong in this?
Working:
function Convertfunction(sTest:String):Variant;
begin
result:= StrtoIntDef(sTest,0);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
sString: string;
value:variant;
begin
sString:= '123456';
value:= Convertfunction(sString);
showMessage(value);
end;
Not Working:
function Convertfunction(sTest:String):Variant;
begin
result:= StrtoInt64Def(sTest,0);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
sString: string;
value:variant;
begin
sString:= '12345678901';
value:= Convertfunction(sString);
showMessage(value);
end;

Delphi 5 doesn't seem to have stock converter between int64 and variant. If you need it in your app you can
implement it (see manual on "custom variant types")
switch to more recent Delphi
switch to CodeTyphon or other Lazarus/FPC distro

Related

Other Ways to Check if String is a Regular Expression

I have this function to check if a string is a regular expression and it works fine :
function IsValidRegEx(aString: string): Boolean;
var
aReg : TRegEx;
begin
Result := False;
if Trim(aString) = '' then
begin
Exit;
end;
try
aReg := TRegEx.Create(aString);
if aReg.IsMatch('asdf') then
begin
end;
Result := True;
except
end;
end;
the problem is it always raise a debugger exception notification if string value is false. I want to eliminate that notification. There is an option to ignore that exception in the notification itself but I don't want it. As much as possible it would be the codes that will adjust.
If you want to use this approach, then you can't avoid exceptions being raised by the Delphi regex library. You'd need to dig down to the PCRE library that Delphi uses to implement its regex library. For instance:
{$APPTYPE CONSOLE}
uses
System.RegularExpressionsAPI;
function IsValidRegEx(const Value: UTF8String): Boolean;
var
CharTable: Pointer;
Options: Integer;
Pattern: Pointer;
Error: PAnsiChar;
ErrorOffset: Integer;
begin
CharTable := pcre_maketables;
Options := PCRE_UTF8 or PCRE_NEWLINE_ANY;
Pattern := pcre_compile(PAnsiChar(Value), Options, #Error, #ErrorOffset, CharTable);
Result := Assigned(Pattern);
pcre_dispose(Pattern, nil, CharTable);
end;
begin
Writeln(IsValidRegEx('*'));
Writeln(IsValidRegEx('.*'));
Readln;
end.
Note that I have written this with Delphi XE7, as I don't have access to XE2. If this code doesn't compile, then it should not be too hard to study the source code for the Delphi regex library to work out how to achieve the same in XE2.

How to get address of untyped const data?

In a common Delphi pattern, i am passing a value as an untyped const to a function:
procedure DoSomething(const Something; SomethingLength: Integer);
begin
//...
end;
In this example, i happen to be passing Windows FORMATETC structure:
procedure Test;
var
omgp: TFormatEtc;
begin
omgp := Default(TFormatEtc);
omgp.cfFormat := RegisterClipboardFormat('CF_PNG');
omgp.ptd := nil;
omgp.dwAspect := DVASPECT_CONTENT or DVASPECT_THUMBNAIL;
omgp.lindex := -1; //all pages
omgp.tymed := TYMED_HGLOBAL;
DoSomething(omgp, SizeOf(omgp));
end;
I need to get the address of this data, so i can pass it to an underlying Windows function something that requires the pointer to the data.
In order to do this, i have always used Pointer(#data):
procedure DoSomething(const Something; SomethingLength: Integer);
begin
SomethingThatNeedsAPointer(Pointer(#Something));
end;
Until this one API call, in one particular case, is failing (it's returning the wrong values). For no particular reason, I happened to look closely at the pointer value being passed. When i was checking everythe parameter value in the debugger i noticed something horrifying. I noticed that:
#Something
Pointer(#Something)
return different values.
#Something should already be a pointer
Pointer(#Something) should be a redundant cast
Which way is the right way to get the address of untyped data?
Edit: People went to lunch on something unrelated to the question. I've edited the question so that hopefully people will focus on the question, and not the example.
This is a debugger bug, whereby the debugger is mis-reporting the value of Pointer(#Salt). I can reproduce the fault in XE5, XE6 and XE7, but not in XE4 and XE8. So it seems that this is a defect introduced in XE5 and removed in XE8.
Whenever you see an issue like this, a debugger fault is always a possibility. In this case we can demonstrate that the fault lies in the debugger with this program:
{$APPTYPE CONSOLE}
uses
System.SysUtils;
procedure DoSomething(const Salt; SaltLength: Integer);
begin
Writeln(IntToHex(NativeUInt(Pointer(#Salt)), 8));
Writeln(IntToHex(NativeUInt(#Salt), 8));
end;
procedure Test;
var
salt: AnsiString;
begin
salt := 'salt';
DoSomething(salt[1], Length(salt));
end;
begin
Test;
end.
This program outputs:
007C9BD4
007C9BD4
even though #Salt and Pointer(#Salt) are accorded different values by the debugger.
Note that this program
{$APPTYPE CONSOLE}
procedure DoSomething(const Salt; SaltLength: Integer);
var
i: Integer;
P: PAnsiChar;
begin
P := #Salt;
for i := 0 to SaltLength-1 do
begin
Writeln(P^);
inc(P);
end;
end;
procedure Test;
var
salt: AnsiString;
begin
salt := 'salt';
DoSomething(salt[1], Length(salt));
end;
begin
Test;
end.
outputs:
s
a
l
t
I cannot reproduce your case in XE8.
The debugger shows same address for both #Salt and Pointer(#Salt).
Likewise the output of this test snippet is identical.
I can only assume that what the debugger is telling you is untrue somehow.
(Update: A test in XE7 reproduces the error in the debugger. The outcome of the snippet is the same though.)
program Test;
{$APPTYPE CONSOLE}
procedure Test1(p: Pointer);
begin
WriteLn(Cardinal(p));
end;
procedure Test(const Salt);
begin
Test1(#Salt);
Test1(Pointer(#Salt));
Test1(Addr(Salt));
end;
var
s:AnsiString;
begin
s := 'Test';
Test( s[1]);
ReadLn;
end.

Delphi XE2 RTTI broken?

I recently migrated from D2010 to DXE2 and found a showstopper bug (Or feature?) in XE2 and XE3 (Tested in my friend XE3) related to RTTI generation for TBytes fields inside classes.
I found that the RTTI information for a TBytes variable inside a class is never generated.
The following code works well in D2010, but shows the message "Error" in XE2/XE3
Does anyone have any clue? This will totally break all our software data serialization implementation
To test the code please add Rtti unit to the uses declaration
type
TMyClass = class
public
Field1: Integer;
Field2: TBytes;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
i: Integer;
Data: TMyClass;
Rtti: TRttiContext;
RttiClassType: TRttiInstanceType;
begin
Data := TMyClass.Create;
try
// Get the context
Rtti := TRttiContext.Create;
try
// Get the type for the class
RttiClassType := TRttiInstanceType(Rtti.GetType(Data.ClassInfo));
// Check the fields
for i := 0 to High(RttiClassType.GetFields) do
begin
// Check the field type
if not Assigned(RttiClassType.GetFields[i].FieldType) then
ShowMessage('Error');
end;
finally
Rtti.Free;
end;
finally
Data.Free;
end;
end;
The error message will be displayed when checking for Field2 that is a TBytes becayse the FieldType is always nil!!!
Does anyone has any clue of what have changed in the RTTI from D2010 do XE2? Maybe because the TBytes type was changed from array of Byte to the generic array?
You can fix this error (it is actually not the same bug as the one Mason mentioned).
type
FixTypeInfoAttribute = class(TCustomAttribute)
public
FTypeInfo: PPTypeInfo;
constructor Create(TypeInfo: PTypeInfo);
end;
procedure FixFieldType(TypeInfo: PTypeInfo);
var
ctx: TRttiContext;
t: TRttiType;
f: TRttiField;
a: TCustomAttribute;
n: Cardinal;
begin
t := ctx.GetType(TypeInfo);
for f in t.GetFields do
begin
for a in f.GetAttributes do
begin
if (a is FixTypeInfoAttribute) and f.ClassNameIs('TRttiInstanceFieldEx') then
begin
WriteProcessMemory(GetCurrentProcess, #PFieldExEntry(f.Handle).TypeRef,
#FixTypeInfoAttribute(a).FTypeInfo, SizeOf(Pointer), n);
end;
end;
end;
end;
constructor FixTypeInfoAttribute.Create(TypeInfo: PTypeInfo);
begin
FTypeInfo := PPTypeInfo(PByte(TypeInfo) - SizeOf(Pointer));
end;
Then you add the attribute to your class definition:
type
TMyClass = class
private
Field1: Integer;
[FixTypeInfo(TypeInfo(TBytes))]
Field2: TBytes;
end;
and make sure the FixFieldType routine is called:
initialization
FixFieldType(TypeInfo(TMyClass));
Tested on XE
This is a known issue that was fixed in XE3. Unfortunately, upgrading is the only way to get a fix for it; bug fixes don't usually get ported back.
EDIT: Or not. Apparently this is not actually fixed, as it still occurs in XE3. Reporting it as a new case and mentioning 103729 would probably be the best course of action.

Delphi: Return value might be undefined, despite setting it after begin

can anyone tell me why I get "Return value ... might be undefined" here:
function TXMLAcceptorBCOLSubmission.createRecordsInBCFEEPAR(AXML: TRipXMLElement): String;
var
...
begin
Result := '';
I am using Delphi 5 and it looks like the problem is caused by declaring more than 30 variables (I know, I know). It doesn't seem to matter what they are called or what types they are.
Following code doesn't generate a warning using Delphi 5 so
either it is a bug in an other Delphi version (you should mention the version you use)
or either it is something you didn't show us yet.
Code
program ProveAPoint;
{$APPTYPE CONSOLE}
uses SysUtils;
type
TRipXMLElement = record
end;
TXMLAcceptorBCOLSubmission = class
public
function createRecordsInBCFEEPAR(AXML: TRipXMLElement): string;
end;
function TXMLAcceptorBCOLSubmission.createRecordsInBCFEEPAR(AXML: TRipXMLElement): String;
begin
Result := '';
end;
var
AXML: TRipXMLElement;
begin
with TXMLAcceptorBCOLSubmission.Create do
begin
createRecordsInBCFEEPAR(AXML);
Free;
end;
end.

Delphi 2010 variant to unicode problem

I am working on a DLL in Delphi 2010. It exports a procedure that receives an array of variants. I want to be able to take one of these variants, and convert it into a string, but I keep getting ?????
I cannot change the input variable - it HAS to be an array of variants.
The host app that calls the DLL cannot be changed. It is written in Delphi 2006.
Sample DLL code:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: string;
begin
s:= string(String(Arruid[0]));
showmessage(s);
end;
Using D2006 my DLL works fine. I have tried using VartoStr - no luck. When I check the VarType I am getting a varString. Any suggestions how to fix this?
You host application is sending an AnsiString and you dll is expecting a UnicodeString.
Unicode strings was introduced in Delphi 2009, it does not exist in Delphi 2006. How to fix it? Try [untested]:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: AnsiString;
begin
s:= Ansistring(VarToStr(Arruid[0]));
showmessage(s);
end;
or maybe [also untested]:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: AnsiString;
begin
s:= Ansistring(AnsiString(Arruid[0]));
showmessage(s);
end;
You can also check if theres is a function Like VarToStr that accepts AnsiStrings (maybe in the AnsiStrings unit?).
1/ How have you call the VarToStr() function ? VarToString(Arruid[0]) ?
2/ Does your Delphi2006 Application send AnsiString or WideString to the DLL ?
If so, and if (1) is not working, try to cast to AnsiString instead of string.

Resources