代码之家  ›  专栏  ›  技术社区  ›  Adam Haile

C++DLL定义文件中的函数过载

  •  11
  • Adam Haile  · 技术社区  · 16 年前

    我正在编写一个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()时,您是否只使用一个条目,然后指定您想要哪个重载版本?

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

    编辑:将非def(__declspec)方法标记为答案。..我知道这实际上并没有像我想要的那样使用def文件解决问题,但似乎没有使用def文件的(官方)解决方案。然而,如果有人知道我们没有重载函数和def文件的东西,这个问题就没有答案。

    6 回复  |  直到 16 年前
        1
  •  11
  •   Timbo    16 年前

    函数重载是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进行了测试。

        2
  •  10
  •   Graeme Perrow    16 年前

    在代码本身中,使用__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不正确,所以我可以将其标记为官方答案。…它已经足够接近了。

    编辑(格雷姆):哎呀,谢谢你纠正我的拼写错误!

        3
  •  8
  •       16 年前

    我也遇到了类似的问题,所以我也想在这篇文章上发帖。

    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

    从垃圾箱/出口处可以看到

    如果你重载了一个函数名,那么你可能需要明确地说出你想要在.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 了解详情。

        4
  •  3
  •   Christopher    16 年前

    没有官方的方法可以做你想做的事情,因为dll接口是一个C api。

    编译器本身使用损坏的名称作为解决方法,因此当您不想在代码中更改太多时,应该使用名称损坏。

        5
  •  2
  •   Tim Cooper    13 年前

    出口系统税的定义是:

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

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

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

    EXPORTS
    func2=func1
    

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

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

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

        6
  •  2
  •   null    12 年前

    导出重载函数没有语言或版本无关的方法,因为每次编译器发布时,修改约定都会发生变化。

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