代码之家  ›  专栏  ›  技术社区  ›  Aardvark

使用dllexport从dll导出函数

  •  87
  • Aardvark  · 技术社区  · 16 年前

    我想从C++ Window 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风格的全局函数。

    更新

    不包括调用约定(和使用extern“c”)给了我喜欢的导出名称,但这意味着什么?我得到的默认调用约定是PInvoke(.net)、Declare(vb6)和GetProcAddress所期望的吗?(对于getprocaddress,它取决于调用方创建的函数指针)。

    我希望在没有头文件的情况下使用这个dll,因此我不需要太多花哨的定义来让调用者使用头。

    我同意我必须使用一个def文件。

    4 回复  |  直到 8 年前
        1
  •  114
  •   Community CDub    8 年前

    如果您想要纯C导出,请使用C项目而不是C++。C++ DLL依赖于对所有C++ ISM(命名空间等…)的名称篡改。您可以通过在C/C++ +-G.Advestin中进入项目设置来编译C代码,有一个选项“编译为”,它与编译器交换机/TP和/TC协同工作。

    在vc中导出/导入dll libs++

    您真正想做的是在一个头文件中定义一个条件宏,该头文件将包含在您的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生成导出您的函数。

    自从 图书馆出口 不会在使用dll的项目中定义,当该项目包含库的头文件时,将导入所有函数。

    如果您的库是跨平台的,那么您可以在不在Windows上的情况下将库_API定义为Nothing:

    #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是其中推荐的方法。

    从C++DLL中导出unMulink函数用于加载库/pNoCKE

    如果您需要这个来使用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())
      );
    }
    

    下面是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<fnGetEngineVersion *>(
      ::GetProcAddress(m_hDLL, "getEngineVersion")
    );
    m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
      ::GetProcAddress(m_hDLL, "registerPlugin")
    );
    
        2
  •  18
  •   Malick    8 年前

    对于C++:

    我刚刚遇到了同样的问题,我认为值得一提的是,当一个人同时使用两种方法时,会出现一个问题。 __stdcall (或) WINAPI ) extern "C" :

    正如你所知道的 外部“C” 移除装饰,以代替:

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

    获得未修饰的符号名称:

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

    然而 _stdcall (=macro winapi,用于更改调用约定)还修饰名称,以便如果同时使用这两个名称,则可以获得:

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

    以及 外部“C” 因为符号被修饰而丢失(用@bytes)

    注意这个 只有 对于x86体系结构发生,因为 这个 Y-STDCALL 在x64上忽略约定( msdn : 在x64体系结构上,按照惯例,参数在可能的情况下在寄存器中传递,随后的参数在堆栈上传递。 。)

    如果您同时针对x86和x64平台,这就特别棘手。


    两个解决方案

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

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

    #定义导出注释(链接器,”/export:“函数”= γ-函数

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

    #pragma EXPORT
    

    完整例子:

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

    这将为x86和x64目标导出未修饰的函数,同时保留 Y-STDCALL x86公约。这个 __declspec(dllexport) 不是 在这种情况下是必需的。

        3
  •  3
  •   Rytis I    11 年前

    我也有同样的问题,我的解决方案是使用模块定义文件(.def)而不是 __declspec(dllexport) 定义导出( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx )我不知道为什么会这样,但确实如此

        4
  •  -1
  •   Rob K    16 年前

    我认为裸体可能得到你想要的,但它也阻止编译器为函数生成堆栈管理代码。外部“C”导致C样式的名称装饰。删除它,这样就可以去掉你的。链接器不添加下划线,编译器会添加。stdcall导致附加参数堆栈大小。

    更多,请参见: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

    更大的问题是你为什么要这样做?这些乱七八糟的名字怎么了?