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[]);

在德尔福我正在使用:

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;

其中“sink”在某处定义为整数。

我对组装一无所知......所以我没有办法尝试用“asm pop sink; end;”来修复异常。但是“asm pop sink; end;” 确实修复了异常。我第一次看到它是在这篇关于使用 Delphi 制作 XLL 的有用文章中使用的。这是最相关的报价:

“在 Delphi 中,加载项的最大绊脚石是堆栈上返回地址之后的额外参数。每次调用 Excel 时都会免费提供。我从来没有发现它包含什么,但只要你把它扔掉, 您的加载项将正常工作。添加行 asm pop variable, end; 在每次调用后,其中变量可以是任何全局、局部或对象变量,至少 4 字节长-整数很好。重复-这必须是包含在每次 Excel4v 调用之后。否则,您正在构建一个定时炸弹。”

基本上我想了解实际发生了什么,以及为什么。什么可能导致 Win32 函数返回“堆栈上的返回地址之后的额外参数”,这实际上意味着什么?

是否有另一种方法来解决这个问题,例如使用不同的编译器选项或声明函数的不同方式?

调用“asm pop sink; end;”有什么风险吗?每次调用 Excel4v 后...?它似乎工作正常,但是,由于我不明白发生了什么,感觉有点危险......

4

3 回答 3

8

我不相信这是 pascal vs stdcall - 它们是非常相似的调用约定,不应导致函数退出时堆栈不匹配。

从参考文章

这确实是一个非常好的语法,但它与上面的数组定义不同。Array-of 参数是开放数组参数。它们可能看起来像任何数组,并且它们确实接受任何数组,但是它们获得了一个额外的(隐藏的)参数,该参数保存数组中的最高索引(高值)。因为这只是在 Delphi 中,而不是在 C 或 C++ 中,所以你会遇到一个真正的问题。(另请参阅我关于开放数组的文章),因为参数的实际数量不匹配。

您将获得传递给函数的额外“最高数组索引”参数。这是一个 int,必须在函数退出时进行清理,以免堆栈损坏和崩溃。该文章说明了如何将数组传递给 C 函数。

就像是:

type
 PLPXLOPER  = ^LPXLOPER;

并将 PLPXLOPER 作为最后一个参数传递。

于 2009-07-07T16:26:15.740 回答
1

您的调用约定是错误的,特别是“stdcall”。C 声明被指定为“pascal”

Stdcall 以从右到左的顺序传递参数,期望例程进行清理,并且不使用寄存器。Pascal, OTOH按从左到右的顺序传递参数。因此,在任何一种情况下,事情都不会像另一半代码所期望的那样发生。

将您的 Delphi 声明更改为“pascal”而不是“stdcall”。

于 2009-07-07T16:06:23.220 回答
0

大多数 Windows 函数都使用__stdcall作为它们的调用约定

于 2009-07-07T16:04:55.750 回答