Delphi Firebird UDF仅返回一个字符

3

我将使用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';

仅供测试。真正的代码在此行之前。

但是它不起作用。

有谁可以给我建议吗?

哪里出错了?

当我尝试输入一个参数时,会出现一个大错误,如“从连接中读取数据时出错”。

谢谢

Razvan


假设双参数是数字(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';

以下是主题发起人发布的自己的“答案”,作为如何处理浮点数的次要问题。我在这里复制我的评论以供参考:如果您需要另一个问题-请发布另一个问题。最好删除这个“答案”。此外,我怀疑您应该真正提出两个问题:1:Firebird将如何传递Numeric(5,2)值到UDF-使用Firebird的本机C ++约定。然后2:该C ++约定应该用Pascal / Delphi语言命名。显然第一个问题与Delphi无关,而我相信您已经得到了第一个问题的答案,因此第二个问题对您来说应该很明显。 - Arioch 'The
2个回答

1
你选择了错误的数据类型,开玩笑。 StringPCharChar在Delphi中不是数据类型,而是一些实际数据类型的别名。
根据Delphi版本和编译器设置,它们可以是以下类型的快捷方式:
  • 在Delphi 2009+中:UnicodeStringPWideCharWideChar
  • 在默认情况下,使用预Unicode 32位Delphi:AnsiStringPAnsiCharAnsiChar
  • 在16位Delphi或兼容性设置中:ShortStringPAnsiCharAnsiChar
因此,在类型不安全的DLL项目中使用类型别名,你赌注Delphi编译器会为你选择合适的数据类型。这次赌博失败了。 WideCharUnicodeString使用UTF-16字符集,对于非Unicode应用程序(或期望通过协议接收非Unicode数据的应用程序),它们将看起来像零交错字符串,例如(在2009年之前的Delphi术语中)'a'^@'b'^@'c'^@而不是'abc'。根据ASCIIZ(C)字符串约定,这意味着字符串在第一个字符后结束。

因此,您应该使您的DLL接口在两端匹配:您的源代码和Firebird的期望。

  • 如果可能,您可以尝试告诉Firebird那些字段和结果使用UTF-16字符集,但我怀疑Firebird是否支持CSTRING
  • 您可以根据Firebird DLL的期望编写您的DLL,使用数据类型AnsiStringPAnsiCharAnsiChar

我还强烈建议您阅读有关Delphi 2009+中的Unicode及其对兼容性的影响的Delphi帮助,特别是有关类似DLL和指针数学等低级类型不安全事物的文章。Google上也有一些相关文章。

使用DLL可以去除Delphi编译器的类型安全检查,因此您需要确保DLL API两端具有相同的二进制数据表示,这是您的责任。类型别名(如stringcharPChar)会使其非常脆弱且容易出错。
此外,我无法看到您为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如何在CPU级别上存储不同的数据类型,以及它如何从函数中发送/接收它们。 - Arioch 'The
不幸的是,我不擅长低级编程。我现在正在尝试找出如何将数值火鸟值表示为Delphi数据类型。我相信Numeric(5,2)和Numeric(15,5)之间会有差异... - Popa Ovidiu-Razvan
这可能取决于数据库访问库,它将把SQL类型转换为TDataSet VCL类型。 - Arioch 'The

0
据我所知,CSTRING代表以空字符结尾的类C字符串,因此您不能使用与UTF16相关的数据类型。请尝试使用ANSI类型。

很久以前的事情了,所以我不是最新的,但如果你有一个CSTRING类型的输入参数,它被传递为指向ASCIIZ的指针,则没有关于其长度的明确信息-您只能扫描通过此指针解除引用的内存以获取byte=0x00。因此,您无法传递UTF16、UUID等... - pf1957
我不太记得了,所以我不能确定FB是否可以传递不同字符集的CString而不是一些操作系统默认的MBCS。也许它可以,也许不行,FB25距离IB60还有很长的路要走。然而,“从连接中读取数据时出错”表明服务器崩溃,这让我想到了堆栈损坏。因此,如何将输入参数传递给函数可能会导致堆栈崩溃?;-) PS:来吧!我可以用UTF-8编码UUID并完美地传递它。如果我用UTF-16编码它,无论它是UUID还是“ABCDEFG”,只有字符集才是重要的。 - Arioch 'The
广告 UUID:您测试过包含 0x00 值的值吗? - pf1957
这不是它的问题。UUID有不同的表示方式:二进制和文本。对于任何二进制数据,将其伪装成ASCIIZ都是一个坏主意。UUID没有什么特别之处 - 任何类型的二进制数据在这里都是同样糟糕的想法。而且对于文本UUID表示,无论字符集是否允许文本交换都无关紧要。例如,“123.456”作为文本浮点表示形式与文本UUID没有什么特别之处。既然你谈到了“UTF16,UUID” - 文本表示 - UTF-16表示中0x00部分的二进制数据将是0x40、0x00、0x40、0x00。 - Arioch 'The
哦,我明白了。我所指的是FB SQL中特定的VARCHAR格式下的UUID,而我所说的购买UUID则是泛指概念,不涉及任何特定的软件。这可能导致了误解。 - Arioch 'The
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接