Question

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';
Was it helpful?

Solution

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:

  • in Delphi 2009+: UnicodeString, PWideChar, WideChar
  • by default in pre-Unicode 32-bit Delphi: AnsiString, PAnsiChar, AnsiChar
  • in 16-bit Delphi or with compatibility settings: ShortString, PAnsiChar, AnsiChar

So 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.

  • If possible, you may try to say Firebird that those fields and results use UTF-16 charset, but i doubt that Firebird supports it on CSTRING.
  • You can code your DLL according to Firebird DLL expectations using datatypes AnsiString, PAnsiChar, AnsiChar

I 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

OTHER TIPS

AFAIR, CSTRING represents null terminated C-like string, so you cannot use UTF16 related data types. Try use ANSI types instead.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top