【问题标题】:Overloaded functions in C++ DLL def fileC++ DLL def 文件中的重载函数
【发布时间】:2010-09-06 18:25:08
【问题描述】:

我正在编写一个 C/C++ DLL 并想导出我在使用这样的 .def 文件之前完成的某些函数

LIBRARY "MyLib"
EXPORTS
  Foo
  Bar

将代码定义为这样,例如:

int Foo(int a);
void Bar(int foo);

但是,如果我想声明一个 Foo() 的重载方法,比如:

int Foo(int a, int b);

由于 def 文件只有函数名而不是完整的原型,我看不出它如何处理重载函数。您是否只使用一个条目,然后在将正确原型化的函数指针传递给 LoadLibrary() 时指定您想要的重载版本?

编辑:明确地说,这是在使用 Visual Studio 2005 的 Windows 上

编辑:将 non-def (__declspec) 方法标记为答案...我知道这实际上并没有按照我的意愿使用 def 文件解决问题,但似乎没有(官方)解决方案使用def 文件。但是,如果有人知道我们没有重载函数和 def 文件的内容,则将问题悬而未决。

【问题讨论】:

    标签: c++ c dll


    【解决方案1】:

    在代码本身中,使用 __declspec(dllexport) 标记要导出的函数。例如:

    #define DllExport __declspec(dllexport)
    
    int DllExport  Foo( int a ) {
      // implementation
    }
    int DllExport Foo( int a, int b ) {
      // implementation
    }
    

    如果这样做,则无需在 .def 文件中列出函数。

    或者,您可以使用默认参数值,例如:

    int Foo( int a, int b = -1 )
    

    这假定 b 存在一个值,您可以使用它来指示它未使用。如果 -1 是 b 的合法值,或者不存在或不应该是默认值,则这将不起作用。

    编辑(Adam Haile):更正使用 __declspec 因为 __dllspec 不正确,所以我可以将此标记为官方答案......它已经足够接近了。

    编辑(Graeme):糟糕 - 感谢您纠正我的错字!

    【讨论】:

    • 如果我们将 GetProcAddress() 与动态 DLL 一起使用会怎样?
    • 那么你需要使用重命名的函数,或者重命名其中一个函数并将它们都设为extern "C",假设它们都不接受或返回 C++ 对象。
    【解决方案2】:

    函数重载是一项依赖于名称修饰(链接器错误消息中的神秘函数名称)的 C++ 功能。

    通过将损坏的名称写入 def 文件,我可以让我的测试项目链接并运行:

    LIBRARY "TestDLL"
    EXPORTS
        ?Foo@@YAXH@Z
        ?Foo@@YAXHH@Z
    

    似乎有用

    void Foo( int x );
    void Foo( int x, int y );
    

    因此,从错误消息中复制 C++ 函数名称并将它们写入您的 def 文件。但是,真正的问题是:为什么要使用 def 文件而不使用 __declspec(dllexport) ?

    损坏的名称是不可移植的,我用 VC++ 2008 测试过。

    【讨论】:

    • 有趣的方法。非便携式是指跨不同版本的 Visual Studio 吗?这表明他们可能会在版本之间更改名称修改方案?
    • @jxramos 我不确定他们是否真的可以更改名称修改方案。但我怀疑这在切换到另一个编译器时是否会以同样的方式工作,除非那个编译器试图模拟 VC 的行为。
    • 这肯定只是 VC++ 的事情,因为我认为其他编译器不使用 def 文件。另外,如果我记得有人曾经告诉我的是,Microsoft 有这个 dll 接口的想法,其中用户选择的元素通过 def 文件或 __declspec 公开公开,而在带有 *.so 文件的 Unix 中,所有带有公共 API 的内容都会公开。它们在逻辑公共 API 和库的公共 API 之间没有区别。
    【解决方案3】:

    没有一种与语言或版本无关的方式来导出重载函数,因为修改约定会随着编译器的每个版本而改变。

    这就是为什么大多数 WinXX 函数都具有 *Ex 或 *2 之类的有趣名称的原因之一。

    【讨论】:

    • WinXX 评论的有趣背景!
    【解决方案4】:

    没有官方的方式来做你想做的事,因为dll接口是一个C api。

    编译器本身使用重命名作为一种解决方法,因此当您不想对代码进行太多更改时,应该使用重命名。

    【讨论】:

      【解决方案5】:

      我也遇到了类似的问题,所以我也想发帖。

      1. 通常使用

        extern "C" __declspec(dllexport) void Foo();
        

        导出函数名很好。 它通常会导出名称 无需一个 .def 文件。然而,也有一些 __stdcall 函数等异常 和重载的函数名。

      2. 如果你声明一个函数来使用 __stdcall 约定(就像许多 API 函数所做的那样)

        extern "C" __declspec(dllexport) void __stdcall Foo();
        

        将导出一个损坏的名称,例如 _Foo@4。在这种情况下,您可能需要显式映射导出的名称 到内部损坏的名称。

      A.如何导出未损坏的名称。在 .def 文件中添加

      ----
      EXPORTS
          ; Explicit exports can go here
      
          Foo
      -----
      

      这将尝试为内部函数 Foo 找到“最佳匹配”并将其导出。在上述情况下,只有 一个 foo 这将创建映射

      Foo = _Foo@4

      可以通过 dumpbin /EXPORTS 看到

      如果你重载了一个函数名,那么你可能需要在 .def 文件中明确说明你想要哪个函数 通过使用 entryname[=internalname] 语法指定一个损坏的名称。例如

      ----
      EXPORTS
          ; Explicit exports can go here
      
          Foo=_Foo@4
      -----
      

      B. .def 文件的替代方法是您可以使用 #pragma “就地”导出名称。

      #pragma comment(linker, "/export:Foo=_Foo@4")
      

      C.第三种选择是仅将 Foo 的一个版本声明为 extern "C" 以不损坏地导出。详情请见here

      【讨论】:

        【解决方案6】:

        EXPORTS 定义的系统税是:

        entryname[=internalname] [@ordinal [NONAME]] [PRIVATE] [DATA]
        

        entryname 是您要导出的函数或变量名称。这是必需的。如果您导出的名称与 DLL 中的名称不同,请在 DLL 中使用 internalname 指定导出的名称。

        例如,如果您的 DLL 导出一个函数 func1(),并且您希望它用作 func2(),您可以指定:

        EXPORTS
        func2=func1
        

        只需查看错位名称(使用 Dependency walker)并指定您自己的函数名称。

        来源:http://msdn.microsoft.com/en-us/library/hyx1zcd3(v=vs.71).aspx

        编辑:这适用于动态 DLL,我们需要使用 GetProcAddress() 显式获取 Dll 中的函数。

        【讨论】:

          猜你喜欢
          • 2010-09-26
          • 1970-01-01
          • 1970-01-01
          • 2010-09-16
          • 1970-01-01
          • 1970-01-01
          • 2020-02-25
          • 2023-02-22
          相关资源
          最近更新 更多