我正在使用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';
仅供测试。真正的代码就在这一行之前。
但无效。
有人可以建议我吗?哪里出错?
此外,当我尝试输入一个参数时,我收到一个很大的错误,例如“从连接中读取数据时出错”。
Tks
勒兹
这个函数必须假设双参数是数字(5,2)?
function FormatRasaMultiple(const formatRasa, aDenumire, aCod, aSimbol, aTulpina : PAnsiChar; aProcent : Double) : PAnsiChar; cdecl; export;
该函数与previos完全相同但是还有一个数字参数。
到目前为止(我的旧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';
答案 0 :(得分:1)
您选择了错误的数据类型jokers。
String
,PChar
和Char
不是Delphi中的数据类型,而是某些实际数据类型的别名。
取决于Delphi版本,有时在编译器设置上,这些可以是类型的快捷方式:
UnicodeString
,PWideChar
,WideChar
AnsiString
,PAnsiChar
,AnsiChar
ShortString
,PAnsiChar
,AnsiChar
因此,在类型不安全的DLL项目中使用类型别名,您可以认为Delphi编译器会为您选择合适的数据类型。这次赌注失败了。
WideChar
和UnicodeString
使用UTF-16字符集,对于非Unicode应用程序(或对于应用程序,通过协议期望非Unicode数据)看起来像零隔行扫描字符串(在pre中) -2009 Delphi条款)'a'^@'b'^@'c'^@
而不是'abc'
。根据ASCIIZ(C)字符串约定,这意味着字符串在第一个字符后结束。
因此,您应该在两侧使DLL接口匹配:您的源和Firebird期望。
CSTRING
。AnsiString
,PAnsiChar
,AnsiChar
我还强烈建议您阅读Delphi 2009+中关于Unicode的Delphi帮助及其对兼容性的影响,特别是对DLL和指针数学等低级类型不安全的东西。 Google中的主题文章也很少。
使用DLL删除Delphi编译器的类型安全检查,因此您负责确保DLL APi两侧的相同二进制数据表示。键入string
,char
和PChar
等别名会使其非常脆弱且容易出错。
此外,我无法看到您为DLL入口点函数选择的调用约定。您是否使用cdecl
关键字或stdcall
或register
标记了自己的功能?
根据{{1}} cdecl约定判断最可能是正确的,但您应该查看关于此主题的Firebird手册。
http://en.wikipedia.org/wiki/Calling_convention#x86
这适用于32位代码,但对于64位UDF,可能会要求另外一种约定。
现在,更奇怪的是你选择的参数模式。
您的函数具有c:\Program Files (x86)\Firebird\Firebird_2_1\include\ib_util.pas
等参数,这意味着双重间接。
你的函数需要指针 - >指针 - >性格,这似乎不正确或理性。
我在函数参数定义中将var xxx:pointer
限定符更改为var
。
这个样本 - 虽然它被抛弃并且质量有问题(它受到上面提到的头晕别名问题的困扰)也支持const
限定符在这里错误的想法:http://fireudflib.cvs.sourceforge.net/viewvc/fireudflib/src/EMail.pas?view=markup
将这些点组合在一起,我想你的功能看起来应该是
VAR
http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrPLCopy
答案 1 :(得分:0)
AFAIR,CSTRING表示空终止的类C字符串,因此您不能使用与UTF16相关的数据类型。请尝试使用ANSI类型。