Delphi アプリから、Clarion 6.3 で作成された提供された DLL への関数呼び出しをネゴシエートする必要があります。1 つまたは 2 つの文字列パラメーター (2 つのパラメーターを持つ 1 つの関数または 2 つの単一パラメーター関数のいずれか) を渡す必要があります。私たちはすぐに 1 バイトの 0 で終わる文字列 ( char*
C の用語、CSTRING
Clarion の用語、PAnsiChar
Delphi の用語) を使用することに決めました。
私たちが得た実用的な解決策は、32 ビット整数を装った型なしポインタを渡すことでした。Clarion 製の DLL は、Clarion プログラマーが「ピック」または「ピーク」と呼んだものでメモリをトラバースするために使用します。Clarion と Visual Basic の相互運用に関するフォーラム記事もあり、VB から Clarion への文字列の受け渡しに対処し、私の肩の後ろから Clarion 開発者が「コピーは必要ありません。既に知っています。典型的です」。
ただし、低レベルの型指定されていないコードはボイラープレート上ではるかに「リッチ」であり、エラーが発生しやすいため、これは長期的にはより大きな負担になります。型付けされたコードは、より良い解決策を感じるでしょう。
ここで私が求めているのは、「それは、考えずにコピーして貼り付けて物事を機能させるためのパターンです」というよりも - 私たちはすでにそれを持っています - そして理解、フードの後ろで何が起こっているのか、そしてどうすればそれに頼ることができるのか、 Clarion DLL に何を期待すればよいでしょうか。最終的に「偶然に機能する」ソリューションに行き詰まらないようにするため。
私は彼の肩の後ろから Clarion 6.3 のヘルプをちらりと見ていましたが、ヘルプは低レベルの詳細では役に立ちませんでした。クラリオンから DLL を呼び出すことがすべてでしたが、呼び出されることは重要ではありませんでした。また、自分のマシンには Clarion がありません。借りたくありません。また、Clarion 6.3 ランタイムのソースも開発者が利用できないと言われています。
Clarion と VB の間、または Clarion と C# の間の相互運用などの記事は、両方の言語の特異性を融合させ、「ベア メタル」レベルに関する情報が少ないため、役に立ちません。
Google ブックスは「Clarion Tips & Techniques - David Harms」を指摘し、Clarion の経験者にとっては興味深い洞察があるようですが、私は Clarion ゼロです。少なくとも、相互運用を可能にする低レベルの詳細をそこから把握することはできませんでした。
Clarion 6.3 が作成する DLL の「リスト ファイル」を保存するようにする方法はありますか? 標準の *.H ヘッダー ファイルでしょうか?
繰り返しになりますが、期待どおりに動作するのは、Delphi 側でポインタを渡す関数でした (procedure ...(const param1, param2: PAnsiChar); stdcall;
これは Cstdcall void ...(char* p1, char* p2)
に変換され、Clarion では(LONG, LONG), LONG, pascal, RAW
.
この関数は、スタックから 2 つの 32 ビット パラメータを逆の順序で取得し、それらを使用して終了し、EAX レジスタに戻り値 (実際には未使用のガベージ) を渡し、スタックからパラメータをクリアします。stdcall
あいまいな理由で EBX レジスタを保持しているように見えることを除いて、ほぼ正確です。
クラリオン関数のエントリ:
04E5D38C 83EC04 sub esp,$04 ' allocate local vars
04E5D38F 53 push ebx ' ????????
04E5D390 8B44240C mov eax,[esp+$0c]
04E5D394 BBB4DDEB04 mov ebx,$04ebddb4
04E5D399 B907010000 mov ecx,$00000107
04E5D39E E889A1FBFF call $04e1752c ' clear off local vars before use
そしてその出口
00B8D500 8B442406 mov eax,[esp+$06] ' pick return value
00B8D504 5B pop ebx ' ????
00B8D505 83C41C add esp,$1c ' remove local vars
00B8D508 C20800 ret $0008 ' remove two 32-bits params from stack
EBX を使用した説明のつかない操作とガベージ結果を返すことを除いて、期待どおりに動作します。ただし、Clarion ソースでの型指定されていない低レベル操作が必要です。
現在、1 つの文字列パラメーターのみを受け取るとされる関数: Delphi 側で、これprocedure ...(const param1: PAnsiChar); stdcall;
は Cstdcall void ...(char* p1)
に変換され、Clarion では(*CSTRING), LONG, pascal, RAW
.
クラリオン関数のエントリ:
00B8D47C 83EC1C sub esp,$1c ' allocate local vars
00B8D47F 53 push ebx ' ????????
00B8D480 8D44240A lea eax,[esp+$0a]
00B8D484 BB16000000 mov ebx,$00000016
00B8D489 B990FEBD00 mov ecx,$00bdfe90
00B8D48E BA15000000 mov edx,$00000015
00B8D493 E82002FBFF call $00b3d6b8 ' clear off local vars before use
そしてその出口
04E5D492 8B442404 mov eax,[esp+$04] ' pick return value
04E5D496 5B pop ebx ' ????
04E5D497 83C404 add esp,$04 ' remove local vars
04E5D49A C20800 ret $0008 ' remove TWO 32-bits params from stack
ここで印象に残っているのは、関数によって何らかの形で 2 つのパラメーターが予期され、2 番目のパラメーターのみが使用されることです (x86 asm コードの最初のパラメーターへの参照は見られませんでした)。procedure ...(const garbage: integer; const param1: PAnsiChar); stdcall;
C に変換する必要があるとして呼び出された場合、関数は正常に動作するようstdcall void ...(int garbage, char* p1)
です。
この「見えない」パラメーターは、オブジェクト指向言語のメソッド関数の Self/This ポインターのように見えますが、Clarion のプログラマーは、関連するオブジェクトがまったくないことを確信して私に言いました。さらに、彼の「double-int」関数も、目に見えないパラメーターを期待していないようです。
前述の「ヒント」の本では&CSTRING
、&STRING
Clarion の型は、実際にはフードの背後にある 2 つのパラメーター、バッファーへのポインターとバッファー長であると説明されています。ただし、それらがスタック上でどのように具体的に渡されるかについての情報は提供しません。しかし、Clation は、エクスポートされたパラメーター化された&CSTRING
関数を使用して DLL を作成することを拒否したと言われました。
非表示のパラメーターは、Clarion が関数の戻り値を格納したい場所 (Clarion ソースで割り当てがあった場合)、交差stdcall
/PASCAL
規則であると推測できますが、アセンブラーのエピローグ コードは、そのための EAX レジスタの明確な使用を示しています。 'double-LONG' 関数はそれを使用しません。
それで、私は「自分のマシンで動作する」品質のコードを作成しましたが、自発的にガベージ パラメータを挿入することで、その Clarion 関数を正常に呼び出すことができました。したがって、一見関係のないように見える変更の後に、将来突然実行を開始する可能性があります。
その目に見えないパラメータは何ですか?なぜそれがそこで起こり得るのですか?それに何を期待しますか?