pywin32 icon indicating copy to clipboard operation
pywin32 copied to clipboard

SAFEARRAY expose though .NET COM interop does not work with pywin32

Open ghost opened this issue 9 years ago • 4 comments

Hi,

I have a project where an API is exposed through COM. It is implemented in .NET.

I have made a simple sample project (attached) which exposes two interfaces:

[Guid("1C56509C-35D9-4712-839A-A6D5A366EC96")]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Description("An item.")]
public interface IItem
{
    [Description("Get the name.")]
    [DispId(0)]
    string Name { get; }
}

[Guid("C53A82A2-82A8-4185-97F9-4EF97796CAF5")]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Description("Interface for some container.")]
public interface IContainer
{
    [Description("Get the description.")]
    [DispId(0)]
    string Description { get; }

    [Description("One item.")]
    [DispId(1)]
    IItem GetOneItem();

    [Description("Get the items.")]
    [DispId(2)]
    [return: MarshalAs(UnmanagedType.SafeArray)]
    IItem[] GetItems();
}

This corresponds to COM interfaces in the IDL language as:

[
  odl,
  uuid(1C56509C-35D9-4712-839A-A6D5A366EC96),
  version(1.0),
  helpstring("An item."),
  dual,
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "ComTestServer.IItem")    

]
interface IItem : IDispatch {
    [id(00000000), propget]
    HRESULT Name([out, retval] BSTR* pRetVal);
};

[
  odl,
  uuid(C53A82A2-82A8-4185-97F9-4EF97796CAF5),
  version(1.0),
  helpstring("Interface for some container."),
  dual,
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "ComTestServer.IContainer")    

]
interface IContainer : IDispatch {
    [id(00000000), propget]
    HRESULT Description([out, retval] BSTR* pRetVal);
    [id(0x00000001), helpstring("One item.")]
    HRESULT GetOneItem([out, retval] IItem** pRetVal);
    [id(0x00000002), helpstring("Get the items.")]
    HRESULT GetItems([out, retval] SAFEARRAY(IItem*)* pRetVal);
};

When the .NET assembly is registered (either as part of the build process or though "regasm /codebase") it is easy to create instances of the classes in the sample:

import win32com.client c = win32com.client.Dispatch("ComTestServer.Connection") c.Description 'Some container' r = c.GetOneItem() r.Name 'One item'

So far so good. However, when I try to access the array returnd by GetItems(), I run into problems. As expected, I get a tuple with two elements, but the elements are of type IUnknown and I fail to convert them to either IDispatch or IItem.

x = c.GetItems() i = x[0]; i <PyIUnknown at 0x02E56428 with obj at 0x03DDFFF0>

I have verified that I di get a proper SAFEARRAY from .NET both by using this in VBA and in a C++ program. With pywin32 it does not work though.

Any ideas?

I have tried to set the return type of the list in .NET to [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType =VarEnum.VT_DISPATCH)]

but the result is the same.

/Thomas O

Reported by: thriolsson

Original Ticket: pywin32/bugs/712

ghost avatar Jan 26 '16 07:01 ghost

Here are some binaries too.

Original comment by: thriolsson

ghost avatar Jan 26 '16 12:01 ghost

Not much response on this bug report. I would be happy to help out in order to solve this. I have a lot of experience of C++ and COM, but I am not an expert on Python. If someone could give me a hint how to set up a build environment for pywin32 so that I can debug the C++ part in Visual Studio, I could probably solve this. Is there any helpful documentation or hints how to built pywin32 and make it possible to debug in VS?

Original comment by: thriolsson

ghost avatar Mar 14 '16 12:03 ghost

Actually, my .NET example was not a very good illustration. The interface returned does not implement IDispose. For this to work correctly, the class that implements the interface must have "ComVisible(true)" like this:

[ClassInterface(ClassInterfaceType.None)]
[Description("Interface for some container.")]
[Guid("7635C970-35D4-41E7-8856-5C9AAB35F5C5")]
[ComVisible(true)]
public class Item : IItem
{

This does not solve the issue, but it is a step on the way. See also https://sourceforge.net/p/pywin32/feature-requests/114/

Original comment by: thriolsson

ghost avatar Mar 21 '16 07:03 ghost

See also sourceforge.net/p/pywin32/feature-requests/114

This is now https://github.com/mhammond/pywin32/issues/873

Avasam avatar Mar 16 '24 02:03 Avasam