我正在使用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”,但是当我返回时它没有任何 Value .
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';
2 回答
您选择了错误的数据类型jokers .
String
,PChar
和Char
不是Delphi中的数据类型,而是某些实际数据类型的别名 .根据Delphi版本,有时在编译器设置上,这些可以是类型的快捷方式,例如:
在Delphi 2009中
:
UnicodeString
,PWideChar
,WideChar
默认情况下在Unicode前32位Delphi中:
AnsiString
,PAnsiChar
,AnsiChar
_169_在16位Delphi中或兼容性设置:
ShortString
,PAnsiChar
,AnsiChar
因此,在类型不安全的DLL项目中使用类型别名,您可以认为Delphi编译器会为您选择合适的数据类型 . 这次赌注失败了 .
WideChar
和UnicodeString
使用UTF-16字符集,对于非Unicode应用程序(或对于应用程序,通过协议期望非Unicode数据)将看起来像零隔行扫描字符串(在2009年之前的Delphi术语中)'a'^@'b'^@'c'^@
而不是'abc'
. 根据ASCIIZ(C)字符串约定,这意味着字符串在第一个字符后结束 .因此,您应该在两侧使DLL接口匹配:您的源和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把这些点放在一起,我想你的功能应该是这样的
http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrPLCopy
AFAIR,CSTRING表示空终止的类C字符串,因此您不能使用UTF16相关的数据类型 . 请尝试使用ANSI类型 .