我正在编写一个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_export
和my_Cpp_export
。为简单起见,假设每个函数都接受一个双精度数值参数并返回相同的数据类型。下面几节将概述使用这些方法导出每个函数的备选方法。…
文章随后提供了每种方法的示例。
在你的例子中,因为你已经使用了第二种方法而没有得到你想要的结果,你将不得不使用第一种或第三种方法。