使用dllexport从DLL导出函数

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协调.

在VC++中导出/导入DLL库

你真正想要做的是在一个标题中定义一个条件宏,它将包含在你的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/PInvoke的C++ DLL导出未编码的函数

如果您需要使用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)

  • 不要忘记将模块定义文件添加到项目的链接器设置中 - 只是"将现有项目添加到项目中"是不够的! (3认同)

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平台,这一点尤其棘手.


两种解决方案

  1. 使用定义文件.但这会强制您维护def文件的状态.

  2. 最简单的方法:定义宏(参见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) 不会在这种情况下需要.

  • 谢谢你这个重要的提示.我想知道为什么我的64位DLL与32位不同.我发现你的答案比作为答案的答案更有用. (4认同)
  • 我真的很喜欢这种方法。我唯一的建议是将宏重命名为 EXPORT_FUNCTION,因为“__FUNCTION__”宏仅适用于函数。 (2认同)