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.
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:
My collection interface inherits from IEnumerable (but has it's own GUID):
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.On the VB.NET side it looks like this:
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:
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.