EXPORTS
Foo
Bar使用这样定义的代码,例如:12int Fooint a;
void Barint fo"/>

C ++ DLL def文件中的重载函数

C ++ DLL def文件中的重载函数

Overloaded functions in C++ DLL def file

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

1
2
3
4
LIBRARY"MyLib"
EXPORTS
  Foo
  Bar

使用这样定义的代码,例如:

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

但是,如果我想像下面这样声明Foo()的重载方法,该怎么办:

1
int Foo(int a, int b);

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

编辑:要清楚,这是在Windows上使用Visual Studio 2005

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


函数重载是一种C ++功能,它依赖于名称处理(链接器错误消息中的隐秘功能名称)。

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

1
2
3
4
LIBRARY"TestDLL"
EXPORTS
    ?Foo@@YAXH@Z
    ?Foo@@YAXHH@Z

似乎为

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

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

我使用VC ++ 2008进行了测试,其名称是不可移植的。


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

1
2
3
4
5
6
7
8
#define DllExport __declspec(dllexport)

int DllExport  Foo( int a ) {
  // implementation
}
int DllExport Foo( int a, int b ) {
  // implementation
}

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

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

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

假定存在一个b值,您可以使用该值指示未使用。如果-1是b的合法值,或者没有或不应该使用默认值,则此方法将无效。

编辑(Adam Haile):已更正以使用__declspec作为__dllspec是不正确的,因此我可以将其标记为正式答案...它足够接近。

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


我有一个类似的问题,所以我也想发布。

  • 通常使用

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

    导出函数名称就可以了。
    通常会导出名称
    无需准备即可
    .def文件。但是有一些
    __stdcall函数之类的异常
    和重载的函数名称。

  • 如果您声明要使用的函数
    __stdcall约定(就像许多API函数一样),然后

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

    会输出一个变形的名称,例如
    _Foo @ 4。在这种情况下,您可能需要显式映射导出的名称
    改成内部名称。

  • A.如何导出一个完整的名称。在.def文件中添加

    1
    2
    3
    4
    5
    6
    ----
    EXPORTS
        ; Explicit exports can go here

        Foo
    -----

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

    Foo = _Foo @ 4

    可以通过dumpbin / EXPORTS看到

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

    1
    2
    3
    4
    5
    6
    ----
    EXPORTS
        ; Explicit exports can go here

        Foo=_Foo@4
    -----

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

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

    C.第三种选择是仅声明Foo的一个版本作为外部" C",以不加破坏地导出。有关详细信息,请参见此处。


    没有正式的方法可以执行您想要的操作,因为dll接口是C api。

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


    导出定义的系统税是:

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

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

    例如,如果您的DLL导出函数func1()并希望将其用作func2(),则应指定:

    1
    2
    EXPORTS
    func2=func1

    只需查看变形的名称(使用Dependency Walker)并指定您自己的函数名称即可。

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

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


    没有一种语言或版本不可知的方法来导出重载函数,因为随着每个编译器版本的发布,更改约定都会改变。

    这就是大多数WinXX函数具有有趣的名称(如* Ex或* 2)的原因之一。


    推荐阅读