pywin32 icon indicating copy to clipboard operation
pywin32 copied to clipboard

Feature request: Fix CO_CLASS not following default interface

Open thorntonryan opened this issue 3 years ago • 1 comments

Observed Behavior

We ran into an issue invoking a method on a COM object provided by ActiveQt, because PyWin32 decided to pass the object as a VT_UNKNOWN instead of a VT_DISPATCH.

ActiveQt generated a type library of the form:

[
  helpstring("IFoo Interface")
]
dispinterface IIFoo {
    properties:
    methods:
        SAFEARRAY(BSTR) getBar([in] IFilter* p_filter);
};

[
  helpstring("IFilter Class")
]
coclass IFilter {
!    [default] dispinterface IIFilter;
};

[
  helpstring("IFilter Interface")
]
dispinterface IIFilter {
    properties:
    methods:
};

But the following failed:

filter = win32com.client.Dispatch("MyComServer.IFilter")        
Foo = win32com.client.Dispatch("MyComServer.IFoo")
bar = Foo.getBar(filter)

The call to getBar failed because pywin32 decided to pass filter as an IUnknown instead of recognizing it was an IDispatch.

Upcasting is safe to do, but ran into a bug in the ActiveQt implementation that didn't unwrap our VT_UNKOWN object the way it would if VT_DISPATCH had been chosen.

Expected Behavior

If the coclass has a default interface, and that interface is a dispinterface, then pywin32 should use VT_DISPATCH instead of VT_UNKOWN.

In other words, we agree with the noted FIXME:

should probably get default interface for CO_CLASS???

https://github.com/mhammond/pywin32/blob/70ddf693927fa1635f15e9ef41eb1aea37fdf32a/com/win32com/client/build.py#L603-L607

So probably something of the form:

elif typeKind == pythoncom.TKIND_COCLASS:
    coclassdefaultinterface = findCoClassDefaultInterface(...) # walk the type object looking for IMPLTYPEFLAG_FDEFAULT
    return _ResolveType(coclassdefaultinterface, resultTypeInfo)

I can bug a coworker to provide a better pseudo code sketch if needed, and we actually have a reference C++ implementation of findCoClassDefaultInterface I might be able to share if I get approval

Then pywin32 would recognize that our filter should be passed as VT_DISPATCH and avoid the Qt bug.

Workaround

We can work around the problem by forcing pywin32 to use VT_DISPATCH:

filter = win32com.client.Dispatch("MyComServer.IFilter")        
-Foo = win32com.client.Dispatch("MyComServer.IFoo")
+Foo = win32com.client.dynamic.DumbDispatch("MyComServer.IFoo")
bar = Foo.getBar(filter)

Additional Details

Python 3.10 pywin32 304

thorntonryan avatar Aug 19 '22 21:08 thorntonryan

This sounds reasonable to me, but something I'd probably want to do in conjunction changes to one of our tests to ensure the right thing is happening.

mhammond avatar Sep 17 '22 06:09 mhammond