transfer interfaces from delphi dll to vb.net

怎甘沉沦 提交于 2019-12-24 09:01:24

问题


I have a Delphi written DLL that exposes some interfaces to a vb.net application. The interfaces inherit from IUnknown (but this could be changed if required), simplified example:

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

Same interface in vb.net application:

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

This all works ok.

Now I want to transfer a collection of IWindow to vb.net, and in the vb.net application I want to be able to loop through it with a for in loop.

I read that it's possible using IEnumerable/IEnumerator but I don't quite understand how to implement them. Are there any good tutorials about this, specifically showing the declarations on both side? Example code would be great.

Please note that I prefer not create a com dll that should be registered and imported. Currently I export a function that enabled me to obtain an interface.


回答1:


I have got a solution working, but I am not entirely happy with it. I am placing it here as an answer, hopefully comments of other people may improve it or (even better) a better answer will be placed.

I found that the Delphi's declaration (Delphi 2010) of IEnumerator and IEnumerable are not using the stdcall calling convention so I declared them like this:

  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;

My collection interface inherits from IEnumerable (but has it's own GUID):

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

The class implements IEnumerable, IEnumerator and IEnumVariant. I think that I can remove IEnumerator but I need to do more testing before I can confirm.

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

On the VB.NET side it looks like this:

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

I had to override GetEnumerator because if I don't I get a null reference exception and on the Delphi side GetEnumerator() is never called (VMT offset problem?).

The calling code:

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

The Dim Windows as IWindow is required to make it work, without it I get the error: "Object reference not set to an instance of an object."

I tried to use generic in vb.net side:

Shadows Function GetEnumerator() As IEnumerator(Of IDNKWindow)

But it gives the error: Cannot marshal 'return value': Generic types cannot be marshaled.



来源:https://stackoverflow.com/questions/5758479/transfer-interfaces-from-delphi-dll-to-vb-net

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!