Delphi Xe, Win7x64
To request a string resource with known number:
Function GuGetStrRes(Fn:string;Nom:integer):string;
var
h:THandle;
buffer:array [0..255] of Char;
begin
Result:='';
if fileexists(Fn)=false then
exit;
Try
h:=LoadLibraryEx(pchar(Fn),0,LOAD_LIBRARY_AS_DATAFILE);
if h=0 then
exit;
if LoadString(h, Nom, buffer, SizeOf(buffer)) > 0 then
Result:=string(buffer);
FreeLibrary(h);
Except
Try
if h<>0 then
FreeLibrary(h);
except
end;
End;
End;
// Use
Showmessage(GuGetStrRes('c:\windows\system32\shell32.dll',4200));
Question: how to learn ALL numbers of 'string' resources in DLL? For example to receive a array: 11,22,23,24,40000 and so on (they can go not one after another)
Tried so:
...
var
dllname, str:string;
begin
dllname: ='c:\windows\system32\shell32.dll';
str: = ";
For i: = 0 to 10000 do
begin
str: = GuGetStrRes (dllname, i);
if str <> " then
memo1.lines.add (inttostr (i) + ' - ' +str);
end;
end;
But for some reason it causes an error (even the design try-except does not help), when i: = 4201 :(
When i=0..4200 and >4210, all is OK.
To enumerate the resources strings you must use the EnumResourceNames function passing the RT_STRING type.
check this sample.
{$APPTYPE CONSOLE}
uses
Classes,
Windows,
SysUtils;
function EnumResNameProc(hModule : THandle; lpszType, lpszName : PChar; lParam : longint) : boolean; stdcall;
var
Id : LongInt;
Min : Integer;
Max : Integer;
Index : Integer;
Buffer : PWChar;
Stream : TResourceStream;
Len : Word;
begin
if Longint(lpszName)<65535 then
begin
Id:= longint(lpszName);
Writeln(Format('RT_STRING ID %d',[Id]));
Min:=(Id - 1) * 16;
Max:=(Id * 16) - 1;
Stream:=TResourceStream.CreateFromID(hModule,Id,RT_STRING);
try
Buffer:=Stream.Memory;
for Index:=Min to Max do
begin
//determine the length of the string
Len:=Word(Buffer^);
if Len>0 then
begin
Writeln(Format(' String ID %d',[Index]));
Inc(Buffer,Len+1);
end
else
Inc(Buffer);
end;
finally
Stream.Free;
end;
end
else
Writeln(string(lpszName));
Result := true;
end;
procedure EnumerateStringResources(const FileName:string);
var
hModule : Thandle;
restype : byte;
begin
hModule := LoadLibraryEx(PChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE);
if hModule=0 then exit
else
try
EnumResourceNames(hModule, RT_STRING, #EnumResNameProc, 0);
finally
FreeLibrary(hModule);
end;
end;
begin
try
EnumerateStringResources('YourApplication.exe');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
and the output depending of your resources will be somehitng like this
RT_STRING ID 4080
String ID 65264
String ID 65265
String ID 65266
String ID 65267
String ID 65268
String ID 65269
String ID 65270
String ID 65271
String ID 65272
String ID 65273
String ID 65274
String ID 65275
String ID 65276
String ID 65277
String ID 65278
String ID 65279
RT_STRING ID 4081
String ID 65280
String ID 65281
String ID 65282
String ID 65283
String ID 65284
String ID 65285
String ID 65286
String ID 65287
String ID 65288
String ID 65289
String ID 65290
String ID 65291
String ID 65292
String ID 65293
String ID 65294
String ID 65295
UPDATE
I updated the answer to reflect the strings id inside of the string table, the strings are grouped together in bundles of 16. So the first bundle contains strings 0 through 15, the second bundle contains strings 16 through 31, and so on. so the formula to calculate the strings id can be determined in this way
Min:=(Id - 1) * 16;
Max:=(Id * 16) - 1;
for more information you can read this article from Raymond Chen The format of string resources
Related
Is there a way to convert the two-letter Country Codes into their readable counterparts without using external ressources?
e.g. DE -> Germany, AD -> Andorra
It would be great if I could select the target language or it's using the system language, because I'd like to have them in German.
As #Uwe mentioned in his comment, you can use the EnumSystemGeoID and GetGeoInfo functions. The principle is that with EnumSystemGeoID function you'll enumerate geographical location identifiers and by the GetGeoInfo function query if the enumerated identifier's ISO 2-letter country / region code (info type GEO_ISO2) equals to the one of your interest. If so, then you can query for this identifier with the same function either a friendly name (info type GEO_FRIENDLYNAME), or the official name (info type GEO_OFFICIALNAME), return the result and stop the enumeration.
Here is an example code, which might do that (unfortunately, the enumeration function does not support passing custom data, so I've used a global record variable for passing values):
type
TEnumData = record
GeoCode: string;
GeoName: string;
Success: Boolean;
end;
GEOID = type LONG;
GEOTYPE = type DWORD;
GEOCLASS = type DWORD;
SYSGEOTYPE = (
GEO_NATION = $0001,
GEO_LATITUDE = $0002,
GEO_LONGITUDE = $0003,
GEO_ISO2 = $0004,
GEO_ISO3 = $0005,
GEO_RFC1766 = $0006,
GEO_LCID = $0007,
GEO_FRIENDLYNAME= $0008,
GEO_OFFICIALNAME= $0009,
GEO_TIMEZONES = $000A,
GEO_OFFICIALLANGUAGES = $000B,
GEO_ISO_UN_NUMBER = $000C,
GEO_PARENT = $000D
);
SYSGEOCLASS = (
GEOCLASS_NATION = 16,
GEOCLASS_REGION = 14,
GEOCLASS_ALL = 0
);
GEO_ENUMPROC = function(GeoId: GEOID): BOOL; stdcall;
function EnumSystemGeoID(GeoClass: GEOCLASS;
ParentGeoId: GEOID; lpGeoEnumProc: GEO_ENUMPROC): BOOL; stdcall;
external kernel32 name 'EnumSystemGeoID';
function GetGeoInfo(Location: GEOID; GeoType: GEOTYPE;
lpGeoData: LPTSTR; cchData: Integer; LangId: LANGID): Integer; stdcall;
external kernel32 name {$IFDEF UNICODE}'GetGeoInfoW'{$ELSE}'GetGeoInfoA'{$ENDIF};
implementation
var
// I have used this global variable due to a lack of user data parameter for the callback function
EnumData: TEnumData;
function TryGetGeoInfo(GeoId: GEOID; GeoType: GEOTYPE; out Value: string): Boolean;
var
Buffer: string;
BufferLen: Integer;
begin
Result := False;
BufferLen := GetGeoInfo(GeoId, GeoType, LPTSTR(Buffer), 0, 0);
if BufferLen <> 0 then
begin
SetLength(Buffer, BufferLen);
Result := GetGeoInfo(GeoId, GeoType, LPTSTR(Buffer), BufferLen, 0) <> 0;
if Result then
Value := Trim(Buffer);
end;
end;
function EnumGeoInfoProc(GeoId: GEOID): BOOL; stdcall;
var
S: string;
begin
Result := TryGetGeoInfo(GeoId, GEOTYPE(GEO_ISO2), S);
if Result and (S = EnumData.GeoCode) then
begin
// stop the enumeration since we've found the country by its ISO code
Result := False;
// return the success flag and try to return the friendly name of the country to the
// EnumData.GeoName record field; you can optionally query the GEO_OFFICIALNAME
EnumData.Success := TryGetGeoInfo(GeoId, GEOTYPE(GEO_FRIENDLYNAME), EnumData.GeoName);
end;
end;
function TryGetCountryNameByISO2(const Code: string; out Name: string): Boolean;
begin
// here is the brainless part using global record variable (because the function used
// here with its callback does not support passing user data); no, you cannot tune it
// up by making the callback function nested
EnumData.GeoCode := Code;
EnumData.Success := False;
if not EnumSystemGeoID(GEOCLASS(GEOCLASS_NATION), 0, EnumGeoInfoProc) then
RaiseLastOSError;
Result := EnumData.Success;
if Result then
Name := EnumData.GeoName;
end;
And a possible usage:
var
S: string;
begin
if TryGetCountryNameByISO2('DE', S) then
ShowMessage(S);
end;
You can iterate Languages (from Sysutils) and check the Ext property. The corresponding Name property will give you the localized language name.
for I := 0 to Languages.Count - 1 do begin
Writeln(Languages.Ext[I], '=', Languages.Name[I]);
end;
The sublanguage element suggested by Uwe Raabe is helping, but the results aren't that good, because It won't find all ISO codes and sometimes returns something different to a country name, like Simplified Chinese.
function _GetCountryFromISO(const aISO: string): string;
const
cStatement1 = '-(.*)';
cStatement2 = '\((.*?)\)';
var
i: Integer;
match: TMatch;
begin
Result := aISO; // default result if not found
for i := 0 to Languages.Count - 1 do begin
match := TRegEx.Match(Languages.LocaleName[i], cStatement1);
if not match.Success then
Assert(False, '');
if (aISO.Equals(match.Groups[1].Value)) then begin
match := TRegEx.Match(Languages.Name[i], cStatement2);
if not match.Success then
Assert(False, '');
Exit(match.Groups[1].Value);
end;
end;
end;
I am trying to validate a string, where by it can contain all alphebetical and numerical characters, aswell as the underline ( _ ) symbol.
This is what I tried so far:
var
S: string;
const
Allowed = ['A'..'Z', 'a'..'z', '0'..'9', '_'];
begin
S := 'This_is_my_string_0123456789';
if Length(S) > 0 then
begin
if (Pos(Allowed, S) > 0 then
ShowMessage('Ok')
else
ShowMessage('string contains invalid symbols');
end;
end;
In Lazarus this errors with:
Error: Incompatible type for arg no. 1: Got "Set Of Char", expected
"Variant"
Clearly my use of Pos is all wrong and I am not sure if my approach is even the correct way of going about it or not?
Thanks.
You will have to check every single character of the string, if it's contained in Allowed
e.g.:
var
S: string;
const
Allowed = ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_'];
Function Valid: Boolean;
var
i: Integer;
begin
Result := Length(s) > 0;
i := 1;
while Result and (i <= Length(S)) do
begin
Result := Result AND (S[i] in Allowed);
inc(i);
end;
if Length(s) = 0 then Result := true;
end;
begin
S := 'This_is_my_string_0123456789';
if Valid then
ShowMessage('Ok')
else
ShowMessage('string contains invalid symbols');
end;
TYPE TCharSet = SET OF CHAR;
FUNCTION ValidString(CONST S : STRING ; CONST ValidChars : TCharSet) : BOOLEAN;
VAR
I : Cardinal;
BEGIN
Result:=FALSE;
FOR I:=1 TO LENGTH(S) DO IF NOT (S[I] IN ValidChars) THEN EXIT;
Result:=TRUE
END;
If you are using a Unicode version of Delphi (as you seem to be), beware that a SET OF CHAR cannot contain all valid characters in the Unicode character set. Then perhaps this function will be useful instead:
FUNCTION ValidString(CONST S,ValidChars : STRING) : BOOLEAN;
VAR
I : Cardinal;
BEGIN
Result:=FALSE;
FOR I:=1 TO LENGTH(S) DO IF POS(S[I],ValidChars)=0 THEN EXIT;
Result:=TRUE
END;
but then again, not all characters (actually Codepoints) in Unicode can be expressed by a single character, and some characters can be expressed in more than one way (both as a single character and as a multi-character).
But as long as you constrain yourself within these limitations, one of the above functions should be useful. You can even include both, if you add an "OVERLOAD;" directive to the end of each function declaration, as in:
FUNCTION ValidString(CONST S : STRING ; CONST ValidChars : TCharSet) : BOOLEAN; OVERLOAD;
FUNCTION ValidString(CONST S,ValidChars : STRING) : BOOLEAN; OVERLOAD;
Lazarus/Free Pascal doesn't overload pos for that but has "posset" variants in unit strutils for that;
http://www.freepascal.org/docs-html/rtl/strutils/posset.html
Regarding Andreas' (IMHO correct ) remark, you can use isemptystr for that. It was meant to check for strings that only contain whitespace, but it basically checks if a string only contains characters in a set.
http://www.freepascal.org/docs-html/rtl/strutils/isemptystr.html
You can use Regular Expressions:
uses System.RegularExpressions;
if not TRegEx.IsMatch(S, '^[_a-zA-Z0-9]+$') then
ShowMessage('string contains invalid symbols');
I already have a long working on an application that can read information from Micro, some (most) failed more still missing some, I'm working with WMI and Delphi.
The problem I'm facing is that I need to be listed units which belong to each HD, for example: HD1 has drive C:, D: and so on.
thank you
To correlate the Logical Drives and Physical Disks you must use the Win32_DiskDrive class and the Win32_DiskDriveToDiskPartition, Win32_LogicalDiskToPartition ASSOCIATORS classes.
Try this sample.
{$APPTYPE CONSOLE}
uses
SysUtils,
ActiveX,
ComObj,
Variants;
function ListDrives : string;
var
FSWbemLocator : OLEVariant;
objWMIService : OLEVariant;
colDiskDrives : OLEVariant;
colLogicalDisks: OLEVariant;
colPartitions : OLEVariant;
objdiskDrive : OLEVariant;
objPartition : OLEVariant;
objLogicalDisk : OLEVariant;
oEnumDiskDrive : IEnumvariant;
oEnumPartition : IEnumvariant;
oEnumLogical : IEnumvariant;
iValue : LongWord;
DeviceID : string;
begin;
Result:='';
FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
objWMIService := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
colDiskDrives := objWMIService.ExecQuery('SELECT DeviceID FROM Win32_DiskDrive');
oEnumDiskDrive := IUnknown(colDiskDrives._NewEnum) as IEnumVariant;
while oEnumDiskDrive.Next(1, objdiskDrive, iValue) = 0 do
begin
Writeln(Format('DeviceID %s',[string(objdiskDrive.DeviceID)]));
//Escape the `\` chars in the DeviceID value because the '\' is a reserved character in WMI.
DeviceID := StringReplace(objdiskDrive.DeviceID,'\','\\',[rfReplaceAll]);
//link the Win32_DiskDrive class with the Win32_DiskDriveToDiskPartition class
colPartitions := objWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_DiskDrive.DeviceID="%s"} WHERE AssocClass = Win32_DiskDriveToDiskPartition',[DeviceID]));
oEnumPartition := IUnknown(colPartitions._NewEnum) as IEnumVariant;
while oEnumPartition.Next(1, objPartition, iValue) = 0 do
begin
if not VarIsNull(objPartition.DeviceID) then
begin
Writeln(Format(' Partition %s',[string(objPartition.DeviceID)]));
//link the Win32_DiskPartition class with theWin32_LogicalDiskToPartition class.
colLogicalDisks := objWMIService.ExecQuery('ASSOCIATORS OF {Win32_DiskPartition.DeviceID="'+VarToStr(objPartition.DeviceID)+'"} WHERE AssocClass = Win32_LogicalDiskToPartition');
oEnumLogical := IUnknown(colLogicalDisks._NewEnum) as IEnumVariant;
while oEnumLogical.Next(1, objLogicalDisk, iValue) = 0 do
begin
Writeln(Format(' Logical Disk %s',[string(objLogicalDisk.DeviceID)]));
objLogicalDisk:=Unassigned;
end;
end;
objPartition:=Unassigned;
end;
objdiskDrive:=Unassigned;
end;
end;
begin
try
CoInitialize(nil);
try
ListDrives;
finally
CoUninitialize;
end;
except
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Readln;
end.
This will output something like so
DeviceID \\.\PHYSICALDRIVE0
Partition Disk #0, Partition #0
Logical Disk F:
DeviceID \\.\PHYSICALDRIVE1
Partition Disk #1, Partition #0
Logical Disk C:
Using MagWMi wrapper and its one-liner function (the one-liner is slow - but that is not the place where speed is needed).
I used it to make 2-level disks list.
HardDrive 1 -> Letter 1, Letter 2
HardDrive 2 -> Letter 3, Letter 4
NetServer 1 -> Letter 5, Letter 6
NetServer 2 -> Letter 7, Letter 8
Unrecognized -> All other letters
But You have to remember that some disks may be used without any letter.
So that would tell you info about root directories, but not more.
Disk C: may be spawned to several physical disks.
And some physical disks may be used without letter at all.
unit WMI_Helper;
interface
function WMINetDiskName(const disk: string { 'C:' - w/o slash } ): string;
function WMIPhysDiskName(const disk: string { 'C:' - w/o slash } ): string;
function WMIGetVolumeName(const disk: string { 'C:' - w/o slash } ): string;
implementation
uses magwmi, SysUtils, StrUtils, Windows, IOUtils;
function WMIGetProp(const query, param, resultProp: string): string;
begin
if MagWmiGetOneQ(StringReplace(query, '%s', param, []), resultProp, Result) <= 0
then
Result := '';
Result := Trim(Result);
end;
function WMINetDiskName(const disk: string { 'C:' - w/o slash } ): string;
const
req = 'select ProviderName from Win32_MappedLogicalDisk where DeviceID = "%s"';
prop = 'ProviderName';
var
i: integer;
begin
Result := WMIGetProp(req, disk, prop);
If not TPath.IsUNCPath(Result) then
exit('');
i := PosEx('\', TPath.GetPathRoot(Result), 3);
if i <= 0 then
exit('');
SetLength(Result, i - 1);
end;
function WMIPhysDiskName(const disk: string { 'C:' - w/o slash } ): string;
const
resultProp = 'DeviceID';
reqPart = 'ASSOCIATORS OF {Win32_LogicalDisk.DeviceID="%s"} WHERE ResultClass=Win32_DiskPartition';
reqDisk = 'ASSOCIATORS OF {Win32_DiskPartition.DeviceID="%s"} WHERE ResultClass=Win32_DiskDrive';
begin
Result := WMIGetProp(reqPart, disk, resultProp);
if Result > '' then
Result := WMIGetProp(reqDisk, Result, resultProp);
end;
function WMIGetVolumeName(const disk: string { 'C:' - w/o slash } ): string;
const
prop = 'VolumeName';
reqNet = 'select VolumeName from Win32_MappedLogicalDisk where DeviceID = "%s"';
reqPhy = 'select VolumeName from Win32_LogicalDisk where DeviceID = "%s"';
begin
Result := WMIGetProp(IfThen(GetDriveType(PChar(disk)) = DRIVE_REMOTE, reqNet,
reqPhy), disk, prop);
end;
end.
Which is the best method to detect if a string is Base64Encoded or not (using Delphi)?
Best you can do is try to decode it. If the decode fails then the input was not base64 encoded. It the string successfully decodes then the input might have been base64 encoded.
You can check if the string only contains Base64 valids chars
function StringIsBase64(const InputString : String ) : Boolean;
const
Base64Chars: Set of AnsiChar = ['A'..'Z','a'..'z','0'..'9','+','/','='];
var
i : integer;
begin
Result:=True;
for i:=1 to Length(InputString) do
{$IFDEF UNICODE}
if not CharInSet(InputString[i],Base64Chars) then
{$ELSE}
if not (InputString[i] in Base64Chars) then
{$ENDIF}
begin
Result:=False;
break;
end;
end;
The = char is used for padding so you can add an aditional valiation to the function for padded base64 strings checking if the length of the string is mod 4
In addition to RRUZ answer you can also check the length of the string (is it a multiple of 4).
function IsValidBase64(const aValue: string): Boolean;
var
i: Integer;
lValidChars: set of Char;
begin
Result := aValue <> '';
lValidChars := ['a'..'z', 'A'..'Z', '0'..'9', '/', '+'];
//length of string should be multiple of 4
if Length(aValue) mod 4 > 0 then
Result := False
else
for i := 1 to Length(aValue) do
begin
if aValue[i] = '=' then
begin
if i < Length(aValue) - 1 then
begin
Result := False;
Exit;
end
else
lValidChars := ['='];
end
else if not (aValue[i] in lValidChars) then
begin
Result := False;
Break;
end;
end;
end;
Please note that this code is Delphi 7 code and not adjusted for Unicode use.
As was already told here, there is no reliable verification if a certain string is Base64 encoded or not, so even when you consider the input as a valid Base64 encoded string, it doesn't mean the string is actually encoded that way. I'm posting here just another version of a validation function, which according to RFC 4648 verifies:
if the input string is not empty and its length is multiple of 4
if the input string contains at most two padding characters and only at the end of the string
if the input string contains only characters from the Base64 alphabet (see the Page 5, Table 1)
function IsValidBase64EncodedString(const AValue: string): Boolean;
const
Base64Alphabet = ['A'..'Z', 'a'..'z', '0'..'9', '+', '/'];
var
I: Integer;
ValLen: Integer;
begin
ValLen := Length(AValue);
Result := (ValLen > 0) and (ValLen mod 4 = 0);
if Result then
begin
while (AValue[ValLen] = '=') and (ValLen > Length(AValue) - 2) do
Dec(ValLen);
for I := ValLen downto 1 do
if not (AValue[I] in Base64Alphabet) then
begin
Result := False;
Break;
end;
end;
end;
Is there a method in Delphi to check if a string is a number without raising an exception?
its for int parsing.
and an exception will raise if one use the
try
StrToInt(s);
except
//exception handling
end;
function TryStrToInt(const S: string; out Value: Integer): Boolean;
TryStrToInt converts the string S, which represents an integer-type number in either decimal or hexadecimal notation, into a number, which is assigned to Value. If S does not represent a valid number, TryStrToInt returns false; otherwise TryStrToInt returns true.
To accept decimal but not hexadecimal values in the input string, you may use code like this:
function TryDecimalStrToInt( const S: string; out Value: Integer): Boolean;
begin
result := ( pos( '$', S ) = 0 ) and TryStrToInt( S, Value );
end;
var
s: String;
iValue, iCode: Integer;
...
val(s, iValue, iCode);
if iCode = 0 then
ShowMessage('s has a number')
else
ShowMessage('s has not a number');
Try this function StrToIntDef()
From help
Converts a string that represents an integer (decimal or hex notation) to a number with error default.
Pascal
function StrToIntDef(const S: string; Default: Integer): Integer;
Edit
Just now checked the source of TryStrToInt() function in Delphi 2007. If Delphi 7 dont have this function you can write like this. Its just a polished code to da-soft answer
function TryStrToInt(const S: string; out Value: Integer): Boolean;
var
E: Integer;
begin
Val(S, Value, E);
Result := E = 0;
end;
XE4 and newer:
for ch in s do
TCharacter.IsNumber(ch);
Don't forget:
uses System.Character
In delphi 7 you can use the Val procedure. From the help:
Unit: System
Delphi syntax: procedure Val(S; var V; var Code: Integer);
S is a string-type expression; it must be a sequence of characters that form a signed real number.
V is an integer-type or real-type variable. If V is an integer-type variable, S must form a whole number.
Code is a variable of type Integer.
If the string is invalid, the index of the offending character is stored in Code; otherwise, Code is set to zero. For a null-terminated string, the error position returned in Code is one larger than the actual zero-based index of the character in error.
use this function
function IsNumber(N : String) : Boolean;
var
I : Integer;
begin
Result := True;
if Trim(N) = '' then
Exit(False);
if (Length(Trim(N)) > 1) and (Trim(N)[1] = '0') then
Exit(False);
for I := 1 to Length(N) do
begin
if not (N[I] in ['0'..'9']) then
begin
Result := False;
Break;
end;
end;
end;
For older Delphi versions from delphi 5 help example:
uses Dialogs;
var
I, Code: Integer;
begin
{ Get text from TEdit control }
Val(Edit1.Text, I, Code);
{ Error during conversion to integer? }
if Code <> 0 then
MessageDlg('Error at position: ' + IntToStr(Code), mtWarning, [mbOk], 0);
else
Canvas.TextOut(10, 10, 'Value = ' + IntToStr(I));
end;
In some languages decimal separators are different (for example, '.' is used in English and ',' is used in Russian). For these cases to convert string to real number the following procedure is proposed:
function TryStrToFloatMultiLang(const S : String; out Value : Extended) : Boolean;
var
dc : char;
begin
Result := false;
dc := DecimalSeparator;
DecimalSeparator := '.';
try
Result := TryStrToFloat(S, Value);
except
DecimalSeparator := ',';
Result := TryStrToFloat(S, Value);
end;
DecimalSeparator := dc;
end;
Update
As #Pep mentioned TryStrToFloat catch exceptions, but it returns boolean value. So the correct code is:
function TryStrToFloatMultiLang(const S : String; out Value : Extended) : Boolean;
var
dc : char;
begin
Result := false;
dc := DecimalSeparator;
DecimalSeparator := '.';
Result := TryStrToFloat(S, Value);
if not Result then begin
DecimalSeparator := ',';
Result := TryStrToFloat(S, Value);
end;
DecimalSeparator := dc;
end;
When you using procedure
val(s, i, iCode);
and set value xd ....
val('xd', i, iCode)
as a result we obtain: 13
standard unit Variants
function VarIsNumeric(v:Variant):Boolean