1

使用可能な SOAP インポーターには新しすぎる SOAP API を呼び出す必要がある Delphi 7 アプリケーションがあります。私は、D7 が SOAP API を呼び出すには多大な労力が必要であることに満足しています。しかし、私は Delphi XE2 も持っており、SOAP をインポートして非常にうまく呼び出すことができます。そこで、soap インターフェイスの必要な部分を公開する単純な dll ラッパーを XE2 で作成しました。XE プログラムから dll を呼び出すことができます。

Delphi7 では、XE から SOAP API インポート ファイルを取得し、{$SCOPED_ENUMS ON} 定義と、使用できない SOAP ラッパーを呼び出す初期化セクションを削除し、文字列全体をワイド文字列に変更しました。それはコンパイルされます。文字列の受け渡しを機能させ、すべてを stdcall にすることを避けるために、ShareMM を有効にして FastMM を使用しています。

私がこのようにしようとしている理由は、コードの 90% が XE2 SOAP インポーターによって生成されるため、それが機能する場合、SOAP shim のコーディングと保守が非常に簡単になるためです。 D7 アプリを最新の Delphi に移行します。コードはほとんど変更されません。

しかし、実行すると、奇妙な文字列が表示されます (結果としてアクセス違反が発生します)。問題をより明確にするために、SOAP コードを使用しない単純な関数があります。

Delphi7 exe から DelphiXE2 dll にワイド文字列を渡すと、文字列の長さは 2 倍になります (Length() 関数による) が、一致するデータ変換はありません。したがって、D7 のワイド文字列 "123" は XE2 では "1234...." になります。ここで、.... はたまたまスタックにあるガベージです。バイト配列として表示すると、両方とも期待どおり半分ゼロのバイトを持ちます。

XE2 dll から D7 にワイド文字列を渡すと、ミラー効果が得られます。文字列の長さは半分になり、文字列は単純に切り捨てられます ("1234" は "12" になります)。

あなたがそれを要求することを知っているので、コードを貼り付けています。

Delphi XE2 では、次の関数をエクスポートしています。

// testing
function GetString(s:string):string; export;
function AddToString(s:string):string; export;

implementation

function GetString(s:string):string;
begin
  Result := '0987654321';
end;

function AddToString(s:string):string;
begin
  Result := s + '| ' + IntToStr(length(s)) + ' there is more';
end;

Delphi 7 では:

function GetString(s:widestring):widestring; external 'SMSShim.dll';
function AddToString(s:widestring):widestring; external 'SMSShim.dll';

procedure TForm1.btnTestGetClick(Sender: TObject);
var
  s: widestring;
begin
  s := widestring('1234');
  Memo1.Lines.Add(' GetString: ' + GetString(s));
end;

procedure TForm1.btnTestAddClick(Sender: TObject);
var
  s: widestring;
begin
  s := widestring('1234567890');
  Memo1.Lines.Add(' AddToString: ' + AddToString('1234567890'));
end;

D7 実行可能ファイルをホスト アプリとして使用して dll をデバッグし、どちらの側からでも実行できます。デバッガーでパラメーターと戻り値を調べると、上記の結果が得られます。

厄介なことに、delphi7 でインポートを文字列として宣言すると、正しい長さで無効なデータが得られます。示されているように宣言すると、返そうとすると、有効なデータ、間違った長さ、およびアクセス違反が発生します。

すべて stdcall にしても、動作は変わりません。

明らかな解決策は、私が今必要としている機能を正確に公開する単純なラッパー関数を作成することです。私はそれを行うことができますが、上記の狡猾な方法を好みます。

4

2 に答える 2

2

Delphi XE2 (または 2009 以降のバージョン) で UNICODE を無効にする方法はありませんが、アプリケーションの移行に役立つ多くのリソースがあります。

于 2013-02-21T06:34:05.397 に答える
1

問題の DLL は、パラメーターを受け取ることを期待する関数をエクスポートしUnicodeStringます。(ご存じのとおり、string型は Delphi 2009 でエイリアスになりましたUnicodeString。)Delphi 7 アプリケーションはその DLL を使用できません。Delphi 7 が公開された 2002 年には存在しなかったため、ランタイム ライブラリはその型を操作する方法を知りません。

の文字サイズはUnicodeStringと互換性がありWideStringますが、同じ種類ではありません。UnicodeStringは newAnsiStringのように構造化されているため、長さフィールド、参照カウント、文字サイズ、およびコード ページがあります。WideString長さフィールドがありますが、それが運ぶ他のメタデータは文書化されていません。COMタイプWideStringを公開する Delphi の方法にすぎません。BSTR

従うべき一般的なルールは、C で使用できない DLL 関数をエクスポートしないことですstringWideStringそのBSTRルーツ。

WideStringの代わりにそのパラメーターに使用するように DLL を変更しますstring


1 C との互換性を維持することは、C がサポートする呼び出し規約を使用することも意味します。Delphi のデフォルトのregister呼び出し規約は Microsoft C ではサポートされていないため、これまでに使用したすべての Windows DLL で見られるように、代わりにcdeclorを使用してください。stdcall

于 2013-02-21T18:46:32.360 に答える