I'm using Firebird 2.5.
I'm trying to write a Delphi XE2 UDF to return some string from some input params.
Here is the function in Delphi:
function FormatRasaSingle(var formatRasa : PChar; var aDenumire : PChar; var aCod : PChar; var aSimbol : PChar; var aTulpina : PChar) : PChar;
var tmpString : String;
tmpInteger : Integer;
begin
tmpString := formatRasa;
tmpString := StringReplace(tmpString, '#DENUMIRE', aDenumire, [rfReplaceAll, rfIgnoreCase]);
tmpString := StringReplace(tmpString, '#COD', aCod, [rfReplaceAll, rfIgnoreCase]);
tmpString := StringReplace(tmpString, '#SIMBOL', aSimbol, [rfReplaceAll, rfIgnoreCase]);
tmpString := StringReplace(tmpString, '#TULPINA', aTulpina, [rfReplaceAll, rfIgnoreCase]);
tmpString := 'MAMA';
tmpInteger := Length(tmpString) + 1;
Result := ib_util_malloc(tmpInteger);
StrPCopy(Result, tmpString);
end;
the udf declaration is:
DECLARE EXTERNAL FUNCTION FORMATRASASINGLE
CSTRING(255),
CSTRING(255),
CSTRING(255),
CSTRING(255),
CSTRING(255)
RETURNS CSTRING(255) FREE_IT
ENTRY_POINT 'FormatRasaSingle' MODULE_NAME 'eliteSoftFirebird';
when i'm trying to test the function result only the first letter "M".
the lines
tmpString := 'MAMA';
is for test only. The real code is before this line.
but is not working.
can someone advice me?
where is the mistake?
Also when I try to input one parameter I get a big error like "error reading data from the connection".
Tks
Razvan
how this function must be assuming the double parameters is Numeric (5, 2)?
function FormatRasaMultiple(const formatRasa, aDenumire, aCod, aSimbol, aTulpina : PAnsiChar; aProcent : Double) : PAnsiChar; cdecl; export;
the function is exactly the same as previos one BUT have one more numeric parameter.
so far (my old UDF Firebird 1.5) I was using the declaration as "var aProcent : Double" but when I return it has no value.
the UDF is declared like this:
DECLARE EXTERNAL FUNCTION FORMATRASAMULTIPLE
CSTRING(255),
CSTRING(255),
CSTRING(255),
CSTRING(255),
CSTRING(255),
NUMERIC(5, 2)
RETURNS CSTRING(255) FREE_IT
ENTRY_POINT 'FormatRasaMultiple' MODULE_NAME 'eliteSoftFirebird';
You chosen wrong datatype jokers.
String, PChar, and Char are not data types in Delphi, but aliases to some actual data types.
Depending on Delphi version and sometimes on compiler settings those can be shortcuts to types such has:
UnicodeString, PWideChar, WideCharAnsiString, PAnsiChar, AnsiCharShortString, PAnsiChar, AnsiCharSo using type aliases in type-unsafe DLL project, you gambled that Delphi compiler would choose suitable data types for you. This time the bet failed.
WideChar and UnicodeString use UTF-16 charset, that for non-Unicode application (or for application, expecting non-Unicode data by protocol) would look as zero-interlaced strings like (in pre-2009 Delphi terms) 'a'^@'b'^@'c'^@ instead of 'abc'. This, according to ASCIIZ (C) string convention means the string ends after the 1st character.
Hence, you should make you DLL interface match on both side: your sources and Firebird expectations.
CSTRING.AnsiString, PAnsiChar, AnsiCharI also strongly suggest you reading Delphi help about Unicode in Delphi 2009+ and its implication on compatibility, especially on low-level type-unsafe things like DLLs and pointer maths. There are also few articles on topic in Google.
Using DLLs you remove type safety checks of Delphi compiler, and thus you take the responsibility of ensuring identical binary data representations on both sides of DLL APi. Type aliases like string, char and PChar would make it very fragile and error prone.
Additionally i cannot see a calling convention you selected for your DLL entrypoint function. Did you marked your function with cdecl keyword or stdcall or register ?
Judging by c:\Program Files (x86)\Firebird\Firebird_2_1\include\ib_util.pas cdecl convention is most probably correct one, but you should check Firebird manuals on this topic.
http://en.wikipedia.org/wiki/Calling_convention#x86
That is for 32-bit code however, for 64-bit UDFs some another convention would be probably asked for.
Now, one more strange thing is your choice of parameters' modes.
Your function has parameters like var xxx:pointer and that means double indirection.
Your function expects pointer -> to pointer -> to character and that does not seems correct or rational.
I'd changed var qualifier to const in your function parameters definitions.
This sample - though it is abandoned and of questionable quality (it suffers from the mantioned above dizzy aliases problem) also supports the idea that VAR qualifier is wrong here: http://fireudflib.cvs.sourceforge.net/viewvc/fireudflib/src/EMail.pas?view=markup
Bringing these points together, i guess your function should look like
function FormatRasaSingle(const formatRasa, aDenumire, aCod, aSimbol, aTulpina : PAnsiChar) : PAnsiChar; cdecl; export;
var tmpString : AnsiString;
tmpInteger : Integer;
begin
tmpString := 'MAMA';
Assert( SizeOf(tmpString[1]) = SizeOf(byte) );
tmpInteger := Length(tmpString);
Result := ib_util_malloc(tmpInteger + 1);
StrPLCopy(Result, tmpString, tmpInteger);
end;
http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrPLCopy
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With