Aar*_*ark 97 dll winapi visual-c++ name-decoration
我想要一个从C++ windows DLL导出函数的简单示例.
我想看看标题,cpp文件和def文件(如果绝对需要).
我希望导出的名称不加修饰.我想使用最标准的调用约定(__stdcall?).我想使用__declspec(dllexport)而不必使用DEF文件.
例如:
//header
extern "C"
{
__declspec(dllexport) int __stdcall foo(long bar);
}
//cpp
int __stdcall foo(long bar)
{
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我试图避免链接器为名称添加下划线和/或数字(字节数?).
我没有支持使用相同标头的dllimport和dllexport.我不想要任何有关导出C++类方法的信息,只需要c风格的全局函数.
UPDATE
不包括调用约定(并使用extern"C")给出了我喜欢的导出名称,但这意味着什么?是什么默认调用约定我得到什么pinvoke(.NET),声明(vb6)和GetProcAddress会期望?(我猜对于GetProcAddress,它将取决于调用者创建的函数指针).
我希望在没有头文件的情况下使用这个DLL,所以我真的不需要很多花哨的#defines来使调用者可以使用头文件.
我的回答是我必须使用DEF文件.
jos*_*rry 125
如果要进行纯C导出,请使用C项目而不是C++.C++ DLL依赖于所有C++主题(命名空间等)的名称修改.您可以通过进入C/C++ - > Advanced下的项目设置将代码编译为C,有一个选项"Compile As",它与编译器开关/ TP和/ TC协调.
你真正想要做的是在一个标题中定义一个条件宏,它将包含在你的DLL项目的所有源文件中:
#ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
#else
# define LIBRARY_API __declspec(dllimport)
#endif
Run Code Online (Sandbox Code Playgroud)
然后在您要导出的函数上使用LIBRARY_API:
LIBRARY_API int GetCoolInteger();
Run Code Online (Sandbox Code Playgroud)
在库构建项目中创建一个定义,LIBRARY_EXPORTS这将导致您的函数被导出为您的DLL构建.
由于LIBRARY_EXPORTS不会在使用DLL的项目中定义,因此当该项目包含库的头文件时,将导入所有函数.
如果您的库是跨平台的,那么在Windows上不能将LIBRARY_API定义为任何内容:
#ifdef _WIN32
# ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
# else
# define LIBRARY_API __declspec(dllimport)
# endif
#elif
# define LIBRARY_API
#endif
Run Code Online (Sandbox Code Playgroud)
使用dllexport/dllimport时,您不需要使用DEF文件,如果使用DEF文件,则不需要使用dllexport/dllimport.这两种方法以不同的方式完成相同的任务,我相信dllexport/dllimport是两者中推荐的方法.
如果您需要使用LoadLibrary和GetProcAddress,或者从.NET执行PInvoke,您可以使用extern "C"内联dllexport.因为我们使用的是GetProcAddress而不是dllimport,所以我们不需要从上面进行ifdef跳舞,只需要一个简单的dllexport:
代码:
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
EXTERN_DLL_EXPORT int getEngineVersion() {
return 1;
}
EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
K.getGraphicsServer().addGraphicsDriver(
auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
);
}
Run Code Online (Sandbox Code Playgroud)
以下是Dumpbin/exports的出口情况:
Dump of file opengl_plugin.dll
File Type: DLL
Section contains the following exports for opengl_plugin.dll
00000000 characteristics
49866068 time date stamp Sun Feb 01 19:54:32 2009
0.00 version
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
2 1 00011028 registerPlugin = @ILT+35(_registerPlugin)
Run Code Online (Sandbox Code Playgroud)
所以这段代码工作正常:
m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");
m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
::GetProcAddress(m_hDLL, "registerPlugin")
);
Run Code Online (Sandbox Code Playgroud)
Mal*_*ick 27
对于C++:
我面临着同样的问题,我认为这是值得一提的一个问题出现了,当一个同时使用 __stdcall(或WINAPI)和 extern "C":
如你所知,extern "C"删除装饰,而不是:
__declspec(dllexport) int Test(void) --> dumpbin : ?Test@@YaHXZ
Run Code Online (Sandbox Code Playgroud)
你得到一个未修饰的符号名称:
extern "C" __declspec(dllexport) int Test(void) --> dumpbin : Test
Run Code Online (Sandbox Code Playgroud)
但是_stdcall(=宏WINAPI,它改变了调用约定)也装饰了名称,所以如果我们同时使用它们,我们获得:
extern "C" __declspec(dllexport) int WINAPI Test(void) --> dumpbin : _Test@0
Run Code Online (Sandbox Code Playgroud)
extern "C"因为符号被装饰而丢失的好处(用_ @bytes)
请注意,这仅适用于x86体系结构,因为
__stdcall在x64 上忽略约定(msdn:在x64体系结构上,按照惯例,参数在可能的情况下在寄存器中传递,后续参数在堆栈上传递.).
如果您同时针对x86和x64平台,这一点尤其棘手.
两种解决方案
使用定义文件.但这会强制您维护def文件的状态.
最简单的方法:定义宏(参见msdn):
#define EXPORT注释(链接器,"/ EXPORT:"__ FUNCTION__"="__ FUNCDNAME__)
然后在函数体中包含以下pragma:
#pragma EXPORT
Run Code Online (Sandbox Code Playgroud)
完整示例:
int WINAPI Test(void)
{
#pragma EXPORT
return 1;
}
Run Code Online (Sandbox Code Playgroud)
这将导出x86和x64目标未修饰的函数,同时保留__stdcallx86 的约定.该__declspec(dllexport) 不会在这种情况下需要.