使用 MinGW 和 wclang 交叉编译 DLL 时,我真的需要 __declspec(dllexport) 吗?
Do I really need __declspec(dllexport) when cross-compiling a DLL with MinGW and wclang?
这里有类似的问题,但没有完全回答我的问题:
- When to use __declspec(dllexport) in C++
- Why do I need __declspec(dllexport) to make some functions accessible from ctypes?
当我使用 MinGW 和 wclang 从 Mac OS X 交叉编译 DLL 时,为什么我的 DLL 可以在不使用 [=14= 的情况下正常工作]?
MinGW DLL sample docs,以及我看到的每一个参考,都说在函数声明之前使用 __declspec(dllexport)
。然而,我的 9,000 行库中的 none 代码使用了它,并且 DLL 运行良好!
例如,这是一个以相同方式构建的人为示例库:
#include <stdio.h>
extern "C" {
int hello(const char* name) {
printf("Hello, %s!\n", name);
return 0;
}
}
在 Mac OS X 10.10.3:
上编译
w32-clang++ test.cpp -shared -o test.dll
生成美观的 DLL:
我的 Windows 申请:
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
typedef int(*hellofn)(const char*);
int _tmain(int argc, _TCHAR* argv[])
{
DWORD err;
HINSTANCE dll = LoadLibrary(L"E:\test.dll");
if (!dll) {
err = GetLastError();
std::cout << "Can't load library: " << err << std::endl;
return 1;
}
hellofn hello = (hellofn)GetProcAddress(dll, "hello");
if (!hello) {
err = GetLastError();
std::cout << "Could not load the function: " << err << std::endl;
return 2;
}
int ret = hello("nerd");
std::cout << "hello() returned " << ret << std::endl;
return 0;
}
效果很好:
我是不是搬起石头砸自己的脚,还是有什么魔法我没看到?我在想 wclang (MinGW+clang) 知道以某种方式自动使用 __stdcall
并且不会破坏函数名称?
不,在使用 MinGW 构建 DLL 时,您不需要__declspec(dllexport)
; (事实上 ,我自己经常省略它)。需要注意的是,如果只有一个要包含在 DLL 中的符号被如此装饰,那么您希望导出的所有其他符号 必须 同样被装饰,(除非您通过 --export-all-symbols
构建 DLL 时链接器的选项)。
如果您只包含 未修饰的 符号,那么将导出所有全局符号,就像默认指定 --export-all-symbols
一样。
但是,这与 __stdcall
与 __cdecl
调用约定或名称修改没有任何关系;它只是 DLL 导出中符号可见性的决定因素 table。如果您不将函数声明为 __stdcall
或 __cdecl
,那么它们将默认为 __cdecl
;这没问题,前提是提供 DLL 和调用者都同意该约定。同样,如果双方就任何名称修改约定达成一致(这通常意味着,特别是在 C++ 的情况下,他们在构建时都使用相同的编译器),那么就不会有链接问题。
这里有类似的问题,但没有完全回答我的问题:
- When to use __declspec(dllexport) in C++
- Why do I need __declspec(dllexport) to make some functions accessible from ctypes?
当我使用 MinGW 和 wclang 从 Mac OS X 交叉编译 DLL 时,为什么我的 DLL 可以在不使用 [=14= 的情况下正常工作]?
MinGW DLL sample docs,以及我看到的每一个参考,都说在函数声明之前使用 __declspec(dllexport)
。然而,我的 9,000 行库中的 none 代码使用了它,并且 DLL 运行良好!
例如,这是一个以相同方式构建的人为示例库:
#include <stdio.h>
extern "C" {
int hello(const char* name) {
printf("Hello, %s!\n", name);
return 0;
}
}
在 Mac OS X 10.10.3:
上编译w32-clang++ test.cpp -shared -o test.dll
生成美观的 DLL:
我的 Windows 申请:
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
typedef int(*hellofn)(const char*);
int _tmain(int argc, _TCHAR* argv[])
{
DWORD err;
HINSTANCE dll = LoadLibrary(L"E:\test.dll");
if (!dll) {
err = GetLastError();
std::cout << "Can't load library: " << err << std::endl;
return 1;
}
hellofn hello = (hellofn)GetProcAddress(dll, "hello");
if (!hello) {
err = GetLastError();
std::cout << "Could not load the function: " << err << std::endl;
return 2;
}
int ret = hello("nerd");
std::cout << "hello() returned " << ret << std::endl;
return 0;
}
效果很好:
我是不是搬起石头砸自己的脚,还是有什么魔法我没看到?我在想 wclang (MinGW+clang) 知道以某种方式自动使用 __stdcall
并且不会破坏函数名称?
不,在使用 MinGW 构建 DLL 时,您不需要__declspec(dllexport)
; (事实上 ,我自己经常省略它)。需要注意的是,如果只有一个要包含在 DLL 中的符号被如此装饰,那么您希望导出的所有其他符号 必须 同样被装饰,(除非您通过 --export-all-symbols
构建 DLL 时链接器的选项)。
如果您只包含 未修饰的 符号,那么将导出所有全局符号,就像默认指定 --export-all-symbols
一样。
但是,这与 __stdcall
与 __cdecl
调用约定或名称修改没有任何关系;它只是 DLL 导出中符号可见性的决定因素 table。如果您不将函数声明为 __stdcall
或 __cdecl
,那么它们将默认为 __cdecl
;这没问题,前提是提供 DLL 和调用者都同意该约定。同样,如果双方就任何名称修改约定达成一致(这通常意味着,特别是在 C++ 的情况下,他们在构建时都使用相同的编译器),那么就不会有链接问题。