导出 DLL C++ 类,关于 .def 文件的问题

Exporting DLL C++ Class , question about .def file

提问人:Vhaerun 提问时间:10/9/2008 最后编辑:Gordon WilsonVhaerun 更新时间:10/25/2010 访问量:13832

问:

我想在我的项目中使用隐式链接,而 nmake 真的想要一个 .def 文件。问题是,这是一个类,我不知道在导出部分该写什么。 谁能给我指出正确的方向?

错误消息如下:

NMAKE:U1073:不知道如何制作“DLLCLASS.def”

PS:我正在尝试使用Windows CE Platform Builder进行构建。

C++ 构建 Windows-CE nmake 函数

评论

1赞 paercebal 10/10/2008
使用 .DEF 文件是导出 C++ 代码的最差方式。我希望 Greg Hewgill 描述的 dllimport/dllexport 关键字可以在您的案例中使用,如以下 MS 链接所示: msdn.microsoft.com/en-us/library/ms924233.aspx

答:

8赞 Greg Hewgill 10/9/2008 #1

如果我没记错的话,您可以在上使用,VC++ 将自动为与该类相关的所有符号(构造函数/析构函数、方法、vtable、typeinfo 等)创建导出。__declspec(dllexport)

Microsoft 在此处提供了有关此内容的更多信息。

评论

0赞 Rodrigo Hernandez 9/13/2016
或者仅导出您关心的符号。链接
0赞 Vhaerun 10/9/2008 #2

解决方案如下:

  • 由于导出了类,因此还需要在 .def 文件中添加导出的方法

  • 我没有找到如何导出构造函数,所以我使用了工厂方法(静态),它将返回对象的新实例

  • 其他函数将通过在 .def 文件中添加正常的导出声明来导出

希望有人能从这些信息中受益。

评论

0赞 xtofl 10/9/2008
这不是解决方案,而是解决方法。您可以在 .def 文件中添加构造函数符号。
4赞 xtofl 10/9/2008 #3

始终可以使用 dumpbin /symbols myclass.obj找到成员函数的修饰名称

就我而言

class A {
   public:
     A( int ){}
};

转储显示符号dumpbin??0A@@QAE@H@Z (public: __thiscall A::A(int))

将此符号放在 .def 文件中会导致链接器在导出符号中创建 A::A(int) 符号。

但!正如 @paercebal 在他的评论中所说:手动输入修饰(篡改的)名称是一件苦差事 - 容易出错,而且可悲的是,不能保证在编译器版本之间移植。

4赞 Allbite 3/23/2010 #4

我找到了成为抽象工厂的最佳途径。

首先定义一个纯虚拟基类。这是一个没有实现的类,一个纯粹的虚拟接口类。

您可以导出这个虚拟基“抽象接口”类,但没有真正的理由这样做。当调用方使用它时,他们将通过指针(PImpl 或指向实现的指针)使用它,因此调用方所知道的只是一个简单的内存地址。Def 文件虽然需要多做一些工作,但提供了超出 __declspec(dllexport) 所能达到的好处。你问有什么好处?我们会讲到的,你等着吧。

让你的真实类公开继承自虚拟基地。现在创建一个工厂方法来构造你的对象,并创建一个“release”式的可调用析构函数来执行清理。将这些方法命名为“ConstructMyClass”和“ReleaseMyClass”。请替换“MyClass”:-)

如果这些工厂/发布方法需要任何参数(plain-old-data:integer、char 等),则应仅采用 POD 类型。返回类型应该是虚拟抽象接口基类,或者更确切地说,是指向它的指针。

IMyClass* CreateAnObjectOfTypeIMyClass();

也许现在很明显为什么我们需要虚拟基类?由于虚拟接口类没有实现,它本质上是所有 POD 类型(某种),因此大多数调用者(如 Visual Basic、C 或截然不同的 C++ 编译器)都可以理解该类的“数据类型”。

如果你足够花哨,你可以绕过对“手动发布”方法的需求(对不起,不得不这样做)。如何?通过智能指针和 pImpl 类型的架构管理您自己的类资源,这样当对象死亡时,它就会自行清理。这样做意味着,用我们的圣人和救世主斯科特·迈耶斯(Scott Meyers)的不朽名言来说,你的班级“易于正确使用,难以错误地使用,因为让呼叫者无视清理的需要。让我们中间那些从未忘记称呼“.close”的人投下第一块石头。

也许这种架构听起来很熟悉?它应该,它基本上是 COM 的微型机器版本。嗯,至少是接口、工厂建设和发布的概念。

最后,您已经导出了类的接口,创建(并导出)了 CreateDestroy 方法,现在调用方可以调用 PleaseConstructMyClass 工厂函数,让 DLL 在其接口的幌子下返回一个完全构造、完全实现和完全烘焙的对象。他们可以调用类的所有公共方法(至少是抽象虚拟接口中的方法)并做所有有趣的事情。

当他们完成工厂函数返回的对象时,他们可以调用“ReleaseMyClass”函数来要求 DLL 清理对象的资源,或者你可以通过使类清理自身来帮助他们,使“ReleaseMyClass”方法变得多余且无用。

如果有人对使用 Def 文件和界面的具体收益和权衡感兴趣(除了我的盲目说法),请管道,我们可以更深入地挖掘。

你不就是喜欢这些东西吗?

评论

1赞 audiFanatic 3/4/2016
哇,真是一本好书,我很想进入 DEF 文件的东西。另外,你不会碰巧有实现此体系结构的示例代码吗?这很有趣,但看到代码总是比从英语翻译它更容易哈哈
1赞 Rodrigo Hernandez 9/13/2016
使用 STL 类(如 unique_ptr、shared_ptr、字符串和向量)作为返回或参数类型会破坏所有交叉编译器(想想 MSVC-MinGW)的兼容性吗?