11

我正在编写一个 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 上

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

4

6 回答 6

11

函数重载是依赖于名称修饰(链接器错误消息中的神秘函数名称)的 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 进行了测试。

于 2008-08-25T14:42:03.720 回答
10

在代码本身中,使用 __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 的合法值,或者没有或不应该是默认值,这将不起作用。

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

编辑(格雷姆):哎呀-感谢您纠正我的错字!

于 2008-08-25T14:32:33.180 回答
8

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

  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 看到

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

----
EXPORTS
    ; Explicit exports can go here

    Foo=_Foo@4
-----

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

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

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

于 2008-12-04T21:03:01.843 回答
3

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

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

于 2008-08-27T11:11:20.340 回答
2

没有语言或版本不可知的方式来导出重载函数,因为修改约定可以随着编译器的每个版本而改变。

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

于 2008-08-25T15:54:32.673 回答
2

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 中的函数。

于 2013-03-21T06:15:10.647 回答