3

我正在使用火鸟 2.5。

我正在尝试编写一个 Delphi XE2 UDF 从一些输入参数返回一些字符串。

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;

udf 声明是:

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';

当我试图测试函数结果时,只有第一个字母“M”。

线条

   tmpString := 'MAMA';

仅用于测试。真正的代码在这一行之前。

但不工作。

有人可以建议我吗?

错误在哪里?

此外,当我尝试输入一个参数时,我会收到一个大错误,例如“从连接读取数据时出错”。

Tks

拉兹万


这个函数必须如何假设双参数是数字(5、2)?

function  FormatRasaMultiple(const formatRasa, aDenumire, aCod, aSimbol, aTulpina : PAnsiChar; aProcent : Double) : PAnsiChar; cdecl; export;

该函数与前一个完全相同,但多了一个数字参数。

到目前为止(我的旧 UDF Firebird 1.5)我将声明用作“var aProcent:Double”,但是当我返回时它没有任何价值。

UDF 声明如下:

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';
4

2 回答 2

1

您选择了错误的数据类型小丑。

String, PChar, 和Char不是 Delphi 中的数据类型,而是一些实际数据类型的别名。

取决于 Delphi 版本,有时取决于编译器设置,这些可能是类型的快捷方式,例如:

  • 在德尔福 2009+: UnicodeString, PWideChar,WideChar
  • 默认情况下,在 Unicode 32 位之前的 Delphi 中:AnsiString, PAnsiChar,AnsiChar
  • 在 16 位 Delphi 中或具有兼容性设置: ShortString, PAnsiChar,AnsiChar

因此,在类型不安全的 DLL 项目中使用类型别名,您就认为 Delphi 编译器会为您选择合适的数据类型。这一次赌输了。

WideCharUnicodeString使用 UTF-16 字符集,对于非 Unicode 应用程序(或对于应用程序,通过协议期望非 Unicode 数据)看起来像(在 2009 年之前的 Delphi 术语中)这样的零交错字符串,'a'^@'b'^@'c'^@而不是'abc'. 根据 ASCIIZ (C) 字符串约定,这意味着字符串在第一个字符之后结束。

因此,你应该让你的 DLL 接口在两边都匹配:你的源和 Firebird 的期望。

  • 如果可能,您可以尝试说 Firebird 这些字段和结果使用 UTF-16 字符集,但我怀疑 Firebird 在CSTRING.
  • AnsiString您可以使用数据类型,根据 Firebird DLL 的期望对您的 DLL 进行编码PAnsiCharAnsiChar

我还强烈建议您阅读 Delphi 帮助,了解 Delphi 2009+ 中的 Unicode 及其对兼容性的影响,尤其是关于 DLL 和指针数学等低级类型不安全的东西。谷歌上也很少有关于该主题的文章。

使用 DLL,您可以删除 Delphi 编译器的类型安全检查,因此您有责任确保 DLL APi 两侧的二进制数据表示相同。键入别名,如string,会使其非常脆弱charPChar容易出错。


此外,我看不到您为 DLL 入口点函数选择的调用约定。cdecl您是否使用关键字或stdcall或标记了您的功能register?根据 cdecl 约定判断c:\Program Files (x86)\Firebird\Firebird_2_1\include\ib_util.pas很可能是正确的,但是您应该查看有关此主题的 Firebird 手册。

http://en.wikipedia.org/wiki/Calling_convention#x86

这是针对 32 位代码的,但是对于 64 位 UDF,可能会要求其他一些约定。


现在,更奇怪的事情是您对参数模式的选择。您的函数具有类似的参数var xxx:pointer,这意味着双重间接。您的函数需要指针 -> 指向指针 -> 指向字符,这似乎不正确或不合理。我已将var限定符更改为const您的函数参数定义中的。

这个样本 - 虽然它被遗弃并且质量有问题(它受到上述令人头晕的别名问题的困扰)也支持VAR这里限定符错误的想法:http: //fireudflib.cvs.sourceforge.net/viewvc/fireudflib/src/ EMail.pas?view=markup


把这些点放在一起,我想你的功能应该是这样的

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

于 2013-04-15T09:19:26.867 回答
0

AFAIR,CSTRING 表示以空结尾的类 C 字符串,因此您不能使用 UTF16 相关的数据类型。尝试改用 ANSI 类型。

于 2013-04-15T09:21:03.810 回答