当前位置:  开发笔记 > 后端 > 正文

使用dllexport从DLL导出函数

如何解决《使用dllexport从DLL导出函数》经验,为你挑选了2个好方法。

我想要一个从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;
  }

我试图避免链接器为名称添加下划线和/或数字(字节数?).

我没有支持使用相同标头的dllimport和dllexport.我不想要任何有关导出C++类方法的信息,只需要c风格的全局函数.

UPDATE

不包括调用约定(并使用extern"C")给出了我喜欢的导出名称,但这意味着什么?是什么默认调用约定我得到什么pinvoke(.NET),声明(vb6)和GetProcAddress会期望?(我猜对于GetProcAddress,它将取决于调用者创建的函数指针).

我希望在没有头文件的情况下使用这个DLL,所以我真的不需要很多花哨的#defines来使调用者可以使用头文件.

我的回答是我必须使用DEF文件.



1> joshperry..:

如果要进行纯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

然后在您要导出的函数上使用LIBRARY_API:

LIBRARY_API int GetCoolInteger();

在库构建项目中创建一个定义,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

使用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(new OpenGLGraphicsDriver())
  );
}

以下是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)

所以这段代码工作正常:

m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");

m_pfnGetEngineVersion = reinterpret_cast(
  ::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast(
  ::GetProcAddress(m_hDLL, "registerPlugin")
);


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

2> Malick..:

对于C++:

我面临着同样的问题,我认为这是值得一提的一个问题出现了,当一个同时使用 __stdcall(或WINAPI) extern "C":

如你所知,extern "C"删除装饰,而不是:

__declspec(dllexport) int Test(void)                        --> dumpbin : ?Test@@YaHXZ

你得到一个未修饰的符号名称:

extern "C" __declspec(dllexport) int Test(void)             --> dumpbin : Test

但是_stdcall(=宏WINAPI,它改变了调用约定)也装饰了名称,所以如果我们同时使用它们,我们获得:

   extern "C" __declspec(dllexport) int WINAPI Test(void)   --> dumpbin : _Test@0

extern "C"因为符号被装饰而丢失的好处(用_ @bytes)

请注意,这适用于x86体系结构,因为__stdcall在x64 忽略约定(msdn:在x64体系结构上,按照惯例,参数在可能的情况下在寄存器中传递,后续参数在堆栈上传递.).

如果您同时针对x86和x64平台,这一点尤其棘手.


两种解决方案

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

    最简单的方法:定义宏(参见msdn):

#define EXPORT注释(链接器,"/ EXPORT:"__ FUNCTION__"="__ FUNCDNAME__)

然后在函数体中包含以下pragma:

#pragma EXPORT

完整示例:

 int WINAPI Test(void)
{
    #pragma EXPORT
    return 1;
}

这将导出x86和x64目标未修饰的函数,同时保留__stdcallx86 的约定.该__declspec(dllexport) 不会在这种情况下需要.


谢谢你这个重要的提示.我想知道为什么我的64位DLL与32位不同.我发现你的答案比作为答案的答案更有用.
推荐阅读
黄晓敏3023
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有