我使用的是 Firebird 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';
仅用于测试。真正的代码在这一行之前。
但不起作用。
有人能给我建议吗?
错在哪里?
此外,当我尝试输入一个参数时,我收到一个大错误,例如“从连接读取数据时出错”。
谢谢
拉兹万
<小时/>这个函数如何必须假设双参数是 Numeric (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';
最佳答案
您选择了错误的数据类型 clown 。
String
、PChar
和 Char
不是 Delphi 中的数据类型,而是某些实际数据类型的别名。
根据 Delphi 版本以及有时编译器设置,这些可以是类型的快捷方式,例如:
- 在 Delphi 2009+ 中:
UnicodeString
、PWideChar
、WideChar
- 默认情况下,在 Unicode 之前的 32 位 Delphi 中:
AnsiString
、PAnsiChar
、AnsiChar
- 在 16 位 Delphi 中或具有兼容性设置:
ShortString
、PAnsiChar
、AnsiChar
因此,在类型不安全的 DLL 项目中使用类型别名,您就在赌 Delphi 编译器会为您选择合适的数据类型。这次赌注失败了。
WideChar
和 UnicodeString
使用 UTF-16 字符集,对于非 Unicode 应用程序(或对于协议(protocol)期望非 Unicode 数据的应用程序)来说,它看起来为零 -交错字符串,如(2009 年以前的 Delphi 术语)'a'^@'b'^@'c'^@
而不是 'abc'
。根据 ASCIIZ (C) 字符串约定,这意味着字符串在第一个字符之后结束。
因此,您应该使 DLL 接口(interface)在双方上都匹配:您的来源和 Firebird 的期望。
- 如果可能,您可以尝试告诉 Firebird 这些字段和结果使用 UTF-16 字符集,但我怀疑 Firebird 在
CSTRING
上支持它。 - 您可以使用数据类型
AnsiString
、PAnsiChar
、AnsiChar
根据 Firebird DLL 期望对 DLL 进行编码
我还强烈建议您阅读有关 Delphi 2009+ 中的 Unicode 及其对兼容性的影响的 Delphi 帮助,尤其是关于低级类型不安全的内容,例如 DLL 和指针数学。 Google 上也有几篇关于该主题的文章。
使用 DLL,您可以删除 Delphi 编译器的类型安全检查,因此您有责任确保 DLL APi 两侧的二进制数据表示相同。 string
、char
和 PChar
等类型别名会使其非常脆弱且容易出错。
此外,我看不到您为 DLL 入口点函数选择的调用约定。您是否使用 cdecl
关键字或 stdcall
或 register
标记了您的函数?
根据 c:\Program Files (x86)\Firebird\Firebird_2_1\include\ib_util.pas
判断,cdecl 约定很可能是正确的,但您应该查看有关此主题的 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
关于Delphi Firebird UDF 仅返回一个字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16003652/