I’m trying to instantiate a 3rd party com object (Bloomberg’s blpapicom2.Session.1), which works fine via:
win32com.client.Dispatch (‘blpapicom2.Session.1’)
Except for the fact that it returns the interface of another class (IProviderSession instead of ISession). Instantiating the COM object in excel is not problematic - it returns an object with the correct interface.
I’ve traced through the C++ code of pywincom, and nothing unusual seems to be happening – I get the right CLSID back from PyWinObject_AsIID (I get {CLSID_Session Class}); When GetTypeInfo and GetTypeAttr are called I get the wrong interface (IProviderSession).
Aware that there might be a problem with Bloomberg's COM dll, I wrote my own test in C++ which, I think, makes the calls in the same order as win32com.client.Dispatch:
#include "stdafx.h"
#include <Objbase.h>
int _tmain(int argc, _TCHAR* argv[])
{
CLSID clsid;
CLSID iid = IID_IUnknown;
IUnknown *punk = NULL;
IDispatch *result = NULL;
ITypeInfo *pti = NULL;
TYPEATTR *attr;
DWORD dwClsContext = CLSCTX_SERVER;
CoInitialize(NULL);
LPOLESTR clsName = OLESTR("blpapicom2.Session.1");
HRESULT hr = CLSIDFromString(clsName, &clsid);
SCODE sc = CoCreateInstance(clsid, punk, dwClsContext, iid, (void **)&result);
SCODE sc2 = ((IDispatch *)result)->GetTypeInfo(0, LOCALE_USER_DEFAULT, &pti);
pti->GetTypeAttr(&attr);
return 0;
}
Run from the debugger, attr contains IID_ISession at the end of the program. Inside my Python script PyITypeInfo::GetTypeAttr attr contains IProviderSession. Fearful that something could be happening between python calls, I modified pythoncom_CoCreateInstance to contain:
/* BLOB */
ITypeInfo *pti = NULL;
SCODE sc2 = ((IDispatch *)result)->GetTypeInfo(0, LOCALE_USER_DEFAULT, &pti);
TYPEATTR *attr;
pti->GetTypeAttr(&attr);
before the final return and attr still incorrectly contains IID_IProviderSession.
I've tried this with my current version of Python (2.6.4), a newer one (2.6.8) as well the current release (2.7.3); All are 32bit versions. I'm using Windows 7 64bit. I think all versions as well as my test case were compiled with the 6.0A version of the windows SDK (though I do have 7.0 installed as well). I have an old windows XP machine and it exhibits the same problem (it is running Python 2.6.2), so I don’t think this is a 32/64 bit problem.
Instantiating by GUID has the same problem.
As an aside, I downloaded OLEViewer and that the Session class is implemented as dual - could this be affecting things? Even so, I'm still confused as to why Excel and my sample code work and pythoncom does not...
One issue I see in your c++ code is that you CoCreateInstance with IID_IUnknown, then just cast the result to IDispatch. You should probably either pass IID_IDispatch to CoCreateInstance or explicitly QI for IDispatch after getting the IUnknown. It might be worth experimenting with both of them to see if that changes the behaviour of the object.
Also, another thing you could experiment with is using "makepy" support - ie, use win32com.client.gencache.EnsureDispatch() - this should end up with a makepy generated file where all the interfaces are listed. You should then be able to use win32com.client.CastTo() to get any alternative IDispatch based interfaces from the object.
I doubt the fact the interface is "dual" could be the problem but I can see how the wrong IID to CoCreateInstance could cause that (ie, as an implementation detail, they might always provide an IProviderSession, but when there is a QI for IDispatch they provide the other.