正确的调用约定为Excel VBA导出windows DLL函数,没有混乱的名称



我正在编写一个DLL来导出要在Excel VBA中使用的函数-我已经找到了一种方法,可以通过参数,但有混乱的名称。如果我设置没有名称混淆,那么我就不能传递参数并得到调用约定错误

我使用标准声明来调用从VBA导出的DLL函数:

VBA
Public Declare Function foo Lib "C: ... helloworld.dll" (ByVal bar As Long) As Long

我的函数设置如下:

helloworld.cpp

extern "C" __declspec(dllexport) long foo(long bar){
return bar * 2;
}

我用cl.exe(Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30145 for x86)编译cl.exe /LD helloworld.cpp

dumplib/exports helloworld.dll产率

Dump of file helloworld.dll
File Type: DLL
Section contains the following exports for helloworld.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA      name
1    0 00001000 foo
Summary
2000 .data
6000 .rdata
1000 .reloc
A000 .text

如果我从VBA中调用函数

VBA
dim x as long
x = foo(2)

我得到VBA错误错误DLL调用约定(错误49)

如果我将__stdcall添加到函数签名中,

extern "C" __declspec(dllexport) long __stdcall foo(long bar){
return bar * 2;
}

我得到以下DLL导出

Dump of file helloworld.dll
File Type: DLL
Section contains the following exports for helloworld.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA      name
1    0 00001000 _foo@4
Summary
2000 .data
6000 .rdata
1000 .reloc
A000 .text

如果我在VBA声明中使用别名,函数现在就可以工作了

Public Declare Function foo Lib "C: ... helloworld.dll" Alias "_foo@4" (ByVal bar As Long) As Long
VBA
dim x as long
x = foo(2)
'foo sets x = 4

是否有可能传递参数给函数,但没有一个混乱的/序数名称?

根据微软的文档:

https://learn.microsoft.com/en-us/office/client-developer/excel/developing-dlls

当编译器编译源代码时,通常会根据函数在源代码中的外观更改函数的名称。他们通常通过在名称的开头和/或结尾添加名称来做到这一点,这一过程被称为名称装饰。您需要确保导出的函数具有加载DLL的应用程序可以识别的名称。这意味着告诉链接器将修饰的名称与更简单的导出名称相关联。导出名称可以是源代码中最初出现的名称,也可以是其他名称。

名称的修饰方式取决于语言以及如何指示编译器使函数可用,即调用约定。dll使用的Windows标准进程间调用约定称为WinAPI约定。它在Windows头文件中定义为WINAPI,而它又使用Win32声明器__stdcall来定义。

与Excel一起使用的dll导出函数(无论是工作表函数,宏表等效函数还是用户定义的命令)应始终使用WINAPI/__stdcall调用约定。有必要在函数的定义中显式地包含WINAPI说明符,因为Win32编译器的默认设置是使用__cdecl调用约定,也定义为WINAPIV,如果没有指定的话。

您可以告诉链接器要导出的函数,以及它的名称将以以下几种方式之一被外部知道:

  • 将函数放在EXPORTS关键字后的DEF文件中,并设置DLL项目设置为在链接时引用该文件。
  • 在函数的定义中使用__declspec(dllexport)声明符。
  • 使用#pragma预处理器指令向链接器发送消息。

尽管您的项目可以使用所有这三种方法,并且您的编译器和链接器也支持它们,但您不应该尝试以以上一种方式导出一个函数。例如,假设DLL包含两个源代码模块,一个是C,一个是c++,其中包含两个要导出的函数,分别是my_C_exportmy_Cpp_export。为简单起见,假设每个函数都接受一个双精度数值参数并返回相同的数据类型。下面几节将概述使用这些方法导出每个函数的备选方法。

文章随后提供了每种方法的示例。

在你的例子中,因为你已经使用了第二种方法而没有得到你想要的结果,你将不得不使用第一种或第三种方法。

最新更新