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

テスト専用です。実際のコードはこの行の前にあります。

しかし、動作していません。

誰かが私にアドバイスできますか?

間違いはどこですか?

また、1つのパラメーターを入力しようとすると、「接続からのデータの読み取りエラー」のような大きなエラーが発生します。

Tks

ラズヴァン


double パラメーターが数値 (5, 2) であると仮定すると、この関数はどのようにする必要がありますか?

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

関数は前のものとまったく同じですが、もう1つの数値パラメーターがあります。

これまで(私の古い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

間違ったデータ型のジョーカーを選択しました。

StringPChar、およびCharは、Delphi のデータ型ではなく、実際のデータ型のエイリアスです。

Delphi のバージョンやコンパイラの設定によっては、次のような型へのショートカットになる場合があります。

  • Delphi 2009+: UnicodeString, PWideChar,WideChar
  • Unicode 32 ビット以前の Delphi のデフォルト: AnsiString, PAnsiChar,AnsiChar
  • 16 ビット Delphi または互換性設定 : ShortString、、PAnsiCharAnsiChar

したがって、タイプ セーフでない DLL プロジェクトでタイプ エイリアスを使用すると、Delphi コンパイラが適切なデータ型を選択するという賭けに出ました。今回は賭けに失敗しました。

WideCharUTF-16 文字セットをUnicodeString使用すると、非 Unicode アプリケーション (またはプロトコルによって非 Unicode データを予期するアプリケーション) の場合、(2009 年より前の Delphi 用語で) のようなゼロインターレース文字列として表示され'a'^@'b'^@'c'^@ます'abc'。これは、ASCIIZ (C) 文字列規則によれば、文字列が最初の文字の後に終了することを意味します。

したがって、ソースと Firebird の期待値の両方で DLL インターフェイスを一致させる必要があります。

  • 可能であれば、それらのフィールドと結果が UTF-16 文字セットを使用することを Firebird に伝えようとするかもしれませんが、Firebird が でそれをサポートしているとは思えませんCSTRING
  • データ型を使用してAnsiString、Firebird DLL の期待に従って DLL をコーディングできますPAnsiCharAnsiChar

また、Delphi 2009+ の Unicode に関する Delphi のヘルプと、特に DLL やポインタ演算などの低レベルのタイプセーフでないものに関する互換性への影響について読むことを強くお勧めします。Google のトピックに関する記事もほとんどありません。

DLL を使用すると、Delphi コンパイラのタイプ セーフティ チェックが不要になるため、DLL APi の両側で同一のバイナリ データ表現を保証する必要があります。のようなエイリアスを入力するとstring、非常に壊れやすく、エラーが発生しやすくなります。charPChar


さらに、DLL エントリポイント関数用に選択した呼び出し規約が表示されません。cdecl関数をキーワード orstdcallまたはでマークしましたか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 の場合は、別の規則が必要になる可能性があります。


さて、もう 1 つ奇妙なのは、パラメーターのモードの選択です。関数には次のようなパラメーターがvar xxx:pointerあり、これは二重の間接化を意味します。あなたの関数はポインタ -> へのポインタ -> への文字を期待していますが、それは正しくも合理的でもありません。関数パラメーターの定義でvar修飾子をに変更しました。const

このサンプル - 放棄されており、品質に問題がありますが (上記のめまいエイリアスの問題に悩まされています)、VARここで修飾子が間違っているという考えもサポートしています: http://fireudflib.cvs.sourceforge.net/viewvc/fireudflib/src/ EMail.pas?view=マークアップ


これらのポイントをまとめると、関数は次のようになるはずです

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はnullで終了するCのような文字列を表すため、UTF16関連のデータ型は使用できません。代わりに ANSI 型を使用してみてください。

于 2013-04-15T09:21:03.810 に答える