从Delphi创建一个导出的C++类的实例

3
我跟随Rudy Velthuis的一篇优秀文章,了解如何在DLL中使用C++类。一切都很顺利,除了我需要访问一些没有对应工厂的类。我该如何在DLL中构造一个类的实例?所涉及的类被定义为:
class __declspec(dllexport) exampleClass
{
public:
  void foo();
};

现在没有工厂,我没有明确的方法来实例化这个类,但我知道它是可以做到的,因为我看过 SWIG 脚本(.i文件),让这些类可用于 Python。如果 Python&SWIG 可以做到,那么我认为/希望在 Delphi 中也有一些方法可以实现它。

现在我不太了解 SWIG,但它似乎生成了某种 C++ 混合名称的映射?这大概是对的吗?看着 DLL 的导出项,我想我可以通过索引或直接使用混合名称来访问函数和构造函数/析构函数,但那会很糟糕;而且它甚至能工作吗?即使我能调用构造函数,如何在 Delphi 中做到类似于 "new CClass();"?


只是为了澄清,我正在使用纯虚类(在Delphi中称为纯抽象类)的解决方案。 - Atorian
4个回答

7

Swig的工作方式如下: 1)它为每个类方法创建一个平面API,并构建类工厂/销毁函数。例如:

class C { 
      public : 
           C() {...}
           int M1(int P1) {...}
        } 

它会产生:

       C* New_C();
       Destroy_C(C*self);
       int C_M1(C*self,int P1) {}

这个Flat API需要编译成一个新的DLL。

2) 它会为Flat API生成一个包含Pascal声明的单元。 3) 可以选择使用Flat Pascal API来自动构建Pascal类,例如:

 type TC = class


 private

      FObj : pointer;
 public

      constructor Create();
      destructor Destroy(); override;
      function m1(p1:integer: integer;
    ...

  constructor TC.Create();
  begin
     inherited;
     FObj := New_C();
  end;

  destructor TC.Destroy();
  begin
      Destroy_C(FObj);
      inherited;
  end;

  function TC.M1(P1:integer) : integer;
  begin
      Result := C_M1(FObj, P1);
 end;

非常有用的总结,谢谢。这正是我在从SWIG的Python文件中看到的内容。我注意到它似乎也生成了一个.pyd文件给Python使用,我想这就是你说的平面接口DLL,虽然它是一个奇怪的DLL; 大小巨大但只导出1个init_interfacename函数; 好东西可能只能被Python使用,因为生成的.py文件调用了大量半混淆的interfacename.xxx方法,这些方法肯定不在C++ DLL中。 - Atorian

5
可以使用SWIG来完成这个任务。至少应该是可以的。 我编写了一个针对ObjectPascal的SWIG模块,并在自己的项目中成功使用它。我还翻译了GEOS和GDAL/OGR库。 我在SWIG存储库中也有一个分支,但我仍然需要完成最后的步骤来构建所有测试套件,并修复typemaps以使模块被接受。
有人愿意帮忙吗?
Stefano Moratto stefano.moratto@gmail.com www.csiat.it

我肯定希望在SWIG功能列表中看到这个。对我和许多其他Delphi开发人员来说都非常有用。 - Atorian
同意。Stefano/任何人,如果你还有这段代码,请发一个链接吗?我会把它传到Lazarus/FreePascal论坛www.lazarus.freepascal.org上,因为它可以非常有用地生成FreePascal的C++绑定。 - reiniero
@reiniero 这是你正在寻找的确切链接吗?http://sourceforge.net/tracker/?func=detail&aid=2010931&group_id=1645&atid=301645 - vfclists
@vcflists:感谢提供链接,是的,那就是我们找到的;似乎没有更新的版本。与此同时,这些补丁已经升级到了较新的SWIG版本,并且已经被成功使用。请参见此线程http://www.mail-archive.com/fpc-pascal@lists.freepascal.org/msg30422.html - reiniero

5
正确的方法是编写一个包装 DLL,公开所需类的工厂。
我不确定 SWIG 的工作原理,但任何依赖于逆向工程名称重整的东西都似乎是可疑的方法。
此外,应该只在 C++ 代码中创建 C++ 对象。你应该把对象创建语义留给 C++ 运行时。
COM 存在的原因是为了使跨语言对象隐喻能够完美地工作。
我已经编写了许多 COM 对象,它们被从 Delphi、Python 和 C# 中调用。

啊,用C++写一个包装DLL。谢谢,这绝对会有所帮助。我本来以为可用的解决方案会复杂得多。 - Atorian

2

如果Python可以,这并不意味着任何语言都可以。

Python(或任何其他脚本语言)可能能够访问与其编译器和版本完全相同的C ++代码。

关键测试是Python代码是否可以访问类,无论它是与哪个编译器编译的(例如使用msvc和gcc生成的相同DLL的一个Python实现)

一般来说,所有带有混淆名称的内容都是特定于编译器和版本的。因此,唯一的方法就是使用您要针对的编译器编译测试程序,反汇编它,查找适当的运行时调用(用于类类型,构造函数和运行时助手)以及调用它们的方法,并使用cdecl函数或Delphi中的asm模拟它。

像Rudy这样的熟练人士可能会尽可能地利用Pascal功能并将其呈现为看起来很容易的东西,但高级语言的跨编译器兼容性是一个困难(甚至是不可能的)主题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接