6

Delphiを使用してExcel用のXLLアドインを作成しています。これには、xlcall32.dllのExcel4v関数を何度も呼び出す必要があります。ただし、ここでDelphiの専門家がその特定のAPIを使用したことはほとんどないと推測しているため、他のAPIでも問題が観察された可能性があることを期待しています。

Cでは、特にMicrosoft Excel 2007 XLL SDKに付属のxlcall.hファイルでは、Excel4vは次のように定義されています。

int pascal Excel4v(int xlfn, LPXLOPER operRes, int count, LPXLOPER opers[]);

Delphiで私が使用しているもの:

function Excel4v(xlfn: Integer; operRes: LPXLOPER; count: Integer;
    opers: array of LPXLOPER): Integer; stdcall; external 'xlcall32.dll';

LPXLOPERは、構造体(Cの場合)またはレコード(Delphiの場合)へのポインターです。

私はDelphiでC関数を宣言するための宿題をしてきました(このすばらしい記事は大きな助けになりました)。Excel4vを正しく宣言していると思います。ただし、Delphiコードからその関数を呼び出すと、次の行が続かない限り、例外が発生します(「アクセス違反...」が表示され続けます)。

asm pop sink; end;

ここで、「シンク」はどこかで整数として定義されます。

アセンブリについての手がかりがありません...したがって、「asmpopsink;end;」で例外を修正しようと考える方法はありません。しかし、「asmpopsink;end;」確かに例外を修正します。Delphiを使用したXLLの作成に関するこの便利な記事で最初に使用されたのを見ました。最も関連性の高い引用は次のとおりです。

「Delphiの場合、アドインの大きな障害は、スタックの差出人住所の後の追加パラメータです。これは、Excelを呼び出すたびに無料になります。何が保持されているかはわかりませんが、破棄する限り、アドインは正常に機能します。行asm pop変数を追加し、終了します。変数がグローバル、ローカル、またはオブジェクト変数である可能性があるすべての呼び出しの後に、長さ4バイト以上の整数で問題ありません。繰り返すには、これを行う必要があります。すべてのExcel4v呼び出しの後に含まれます。それ以外の場合は、時限爆弾を作成しています。」

基本的に、実際に何が起こっているのか、そしてその理由を理解したいと思います。Win32関数が「スタック上のリターンアドレスの後に余分なパラメータ」を返す原因は何でしょうか。それは実際にはどういう意味ですか。

これを修正する別の方法がありますか?たとえば、別のコンパイラオプションや、関数を宣言する別の方法を使用しますか?

そして、「asmpopsink;end;」と呼ぶのに危険なことはありますか。Excel4vを呼び出すたびに...?うまくいくようですが、何が起こっているのかわからないので少し危険な感じがします...

4

3 に答える 3

8

それがpascalとstdcallの違いだとは思いません。これらは非常によく似た呼び出し規約であり、関数の終了時にスタックの不一致が発生することはありません。

参照記事から、

これは確かに非常に優れた構文ですが、上記の配列定義と同じではありません。配列のパラメーターは、開いている配列パラメーターです。それらは任意の配列のように見え、任意の配列を受け入れますが、配列内の最高のインデックス(高値)を保持する追加の(非表示の)パラメーターを取得します。これはDelphiでのみ発生し、CやC ++では発生しないため、実際の問題が発生します。(オープン配列に関する私の記事も参照してください)、パラメーターの実際の数が一致しないためです。

関数に渡される余分な「最高の配列インデックス」パラメータを取得しています。これはintであり、関数の終了時にクリーンアップして、スタックが破損してクラッシュしないようにする必要があります。この記事では、配列をC関数に渡す方法を示しています。

何かのようなもの:

type
 PLPXLOPER  = ^LPXLOPER;

そして最後のパラメータとしてPLPXLOPERを渡します。

于 2009-07-07T16:26:15.740 に答える
1

あなたの呼び出し規約、特に「stdcall」は間違っています。C宣言は「pascal」として指定されています

Stdcallはパラメータを右から左の順序で渡し、ルーチンがクリーンアップすることを期待し、レジスタを使用しません。パスカル、OTOHはパラメータを左から右の順序で渡します。したがって、どちらの場合も、コードの残りの半分が期待するように物事が起こっているわけではありません。

Delphi宣言を「stdcall」ではなく「pascal」にも変更します。

于 2009-07-07T16:06:23.220 に答える
0

ほとんどのWindows関数は、呼び出し規約に__stdcallを使用します。

于 2009-07-07T16:04:55.750 に答える