【问题标题】:transfer interfaces from delphi dll to vb.net将接口从 delphi dll 传输到 vb.net
【发布时间】:2011-08-11 03:17:31
【问题描述】:

我有一个 Delphi 编写的 DLL,它向 vb.net 应用程序公开了一些接口。 接口继承自 IUnknown(但如果需要,可以更改),简化示例:

  IWindow = interface(IUnknown)
    ['{E9A11D0B-8A05-4CBA-83FA-C5CC6818DF6E}']
    function GetCaption(var Caption: PChar): HRESULT; stdcall;
  end;

vb.net 应用程序中的相同界面:

<ComImport(), Guid("E9A11D0B-8A05-4CBA-83FA-C5CC6818DF6E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IWindow
    ReadOnly Property Caption() As <MarshalAs(UnmanagedType.LPWStr)> String
End Interface

一切正常。

现在我想将 IWindow 的集合传输到 vb.net,并且在 vb.net 应用程序中,我希望能够使用 for in 循环遍历它。

我读到可以使用 IEnumerable/IEnumerator,但我不太了解如何实现它们。有没有关于这个的好的教程,特别是显示双方的声明?示例代码会很棒。

请注意,我不希望创建应该注册和导入的 com dll。目前我导出了一个函数,使我能够获得一个接口。

【问题讨论】:

  • 您确定 var Caption: PChar 吗?看起来您在 Delphi 中分配内存,但在 .net 代码中释放它。还是我看错了?
  • 它是实现类中char数组的PChar,所以只要接口的refcount> 0,内存就有效。我正在使用接口,所以我不用担心关于分配和释放。
  • 为了它的价值,我会用一个超级简单的 COM BStr 来做这件事。它是 Delphi 端的 WideString 和 .net 端的 UnmanagedType.BStr。它是使用 COM 分配器分配的。我知道这不是问题,但你还是一样。
  • @David Heffernan:出于特定的性能原因,我选择了它。不过,我真的很感谢“思考”!

标签: vb.net delphi com


【解决方案1】:

我有一个可行的解决方案,但我并不完全满意。我把它放在这里作为答案,希望其他人的 cmets 可以改进它,或者(甚至更好)一个更好的答案。

我发现 IEnumerator 和 IEnumerable 的 Delphi 声明(Delphi 2010)没有使用 stdcall 调用约定,所以我这样声明它们:

  IEnumerator = interface(IInterface)
  ['{496B0ABE-CDEE-11D3-88E8-00902754C43A}']
    function MoveNext: Boolean; safecall;
    function GetCurrent: IWindow; safecall;
    function Reset: HResult; stdcall;
  end;

  IEnumerable = interface(IInterface)
  ['{496B0ABE-CDEE-11D3-88E8-00902754C43A}']
    function GetEnumerator(var Enumerator: IEnumerator): HRESULT; stdcall;
  end;

我的集合接口继承自 IEnumerable(但有自己的 GUID):

  IWindows = interface(IEnumerable)
  ['{D9174D5A-4946-4E5A-95B1-2CD521C3BF73}']
  end;

该类实现了 IEnumerable、IEnumerator 和 IEnumVariant。 我认为我可以删除 IEnumerator 但我需要做更多的测试才能确认。

  TIWindows = class(TInterfacedObject, IWindows, IEnumerable, IEnumVariant)

在 VB.NET 方面,它看起来像这样:

<ComImport(), Guid("D9174D5A-4946-4E5A-95B1-2CD521C3BF73"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IWindows
    Inherits IEnumerable
    Shadows Function GetEnumerator() As IEnumerator
End Interface

我必须重写 GetEnumerator,因为如果我不这样做,我会得到一个空引用异常,并且在 Delphi 端 GetEnumerator() 永远不会被调用(VMT 偏移问题?)。

调用代码:

Dim Windows As IWindows
Dim Window As IWindow
Try
    Windows = Session.TopLevelWindows
    For Each Window In Windows
        TextBox1.Text = TextBox1.Text & Window.Caption
    Next
Catch ex As Exception
    ' Handle Exception
End Try

Dim Windows as IWindow 需要使它工作,没有它我得到错误:“对象引用未设置为对象的实例。”

我尝试在 vb.net 端使用泛型:

Shadows Function GetEnumerator() As IEnumerator(Of IDNKWindow)

但它给出了错误:无法封送“返回值”:无法封送泛型类型。

【讨论】:

  • 你为什么不尝试从 Delphi 返回一个安全的 IUnknown 数组呢?我认为它应该更容易,您可能可以将其编组为 vb.net 端的对象数组。
  • 返回一个安全数组有什么好处?可以在 vb.net 中使用 for each 循环遍历它吗?
  • 是的,就是这样。在 Delphi 端生成安全数组可能有点麻烦,但在 vb.net 上使用起来非常容易,就像任何其他对象数组一样。
猜你喜欢
  • 1970-01-01
  • 2014-02-27
  • 1970-01-01
  • 1970-01-01
  • 2022-11-25
  • 2011-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多