import win32com.client, win32com.client.gencache, win32com.client.selecttlb
strPATECLSID = '{9BB3CB79-7098-47D0-83A2-F8F56D236DC8}'
strPATEclassCLSID = '{9A1F3C8E-46B7-4CFA-A820-E4256ECD53B5}'
self.objCATIA = win32com.client.GetActiveObject('CATIA.Application')
win32com.client.gencache.EnsureModule(strPATECLSID, 0, 0, 0)
self.objBase= win32com.client.gencache.GetClassForCLSID(strPATEclassCLSID)
self.__objPATECOM = self.objBase(self.objCATIA)
strApplicationID = <secret>
try:
self.__objPATECOM.SetApplicationID(strApplicationID)
except (pywintypes.com_error), _e:
print "Connection error: %s" %(_e)
return False
====================================================================
When we use win32com 2.16 the code above works fine but when we use win32com 2.17 it throws the error message shown below.
File testKBE_PATECAKE.py, line 367 in ConnectToENOVIA
self.__objPATECOM.SetApplicationID(strApplicationID)
File "C:\Python27\lib\site-packages\win32com\gen_py\9BB3CB79 -7098-47D0-83A2-F8
F56D236DC8x0x0x0.py", line 457 in SetApplicationID
return self._oleobj_.InvokeTypes(1610940417, LCID, 1, (24, 0), ((16396, 1),)
, iAID
File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 516 in
__getattr__
raise AttributeError("%s.%s" % (self._username_, attr))
AttributeEror: CATIA.Application.InvokeTypes
I suspect the problem is that previously you have makepy support generated for the 'CATIA.Application' object which should make things work. The problem is that when you do:
self.__objPATECOM = self.objBase(self.objCATIA)
The DispatchBaseClass.__init__ function in win32com\client\__init__.py checks to see if the object is itself a makepy generated object and will work correctly if it is.
Even though, this really is a bug as we should handle that situation. A fix would probably be to change the line in DispatchBaseClass.__init__ from:
elif isinstance(oobj, DispatchBaseClass):
to
elif hasattr(oobj, "_oleobj_"):
To Mark Hammond:
I don't think the problem is caused by a pre-existing wrapper in the gen_py folder as you suggested in your comment. When I uninstalled version 2.17 of pywin32 and installed version 2.16 on the same PC (with the contents of the gen_py folder unchanged) I was able to call the SetApplicationID method without it throwing an error.
Can you please modify your test code to add:
print self.objCATIA
After creating it, and tell me what it prints in both the failing and working cases?
Sorry for the delay. I added print statements after lines 4, 6, and 7 (in the code shown in my original post). With version 2.16 (pywin32-216.win-amd64-py2.7) I get the following correct output:
self.objCATIA= <COMObject CATIA.Application>
self.objBase= win32com.gen_py.9BB3CB79-7098-47D0-83A2-F8F56D236DC8x0x0x0.PATECOM
self.__objPATECOM= <win32com.gen_py.CATIA V5 BOEPATEAutomationInterfaces Object
Library.PATECOM instance at 0x44540104>
Then I ran the same sript with version 2.17 (pywin32-217.win-amd64-py2.7) and got the following:
self.objCATIA= <COMObject CATIA.Application>
self.objBase= win32com.gen_py.9BB3CB79-7098-47D0-83A2-F8F56D236DC8x0x0x0.PATECOM
self.__objPATECOM= <win32com.gen_py.CATIA V5 BOEPATEAutomationInterfaces Object
Library.PATECOM instance at 0x44540104>
Traceback (moste recent call last):
File testKBE_PATECAKE.py, line 370 in ConnectToENOVIA
self.__objPATECOM.SetApplicationID(strApplicationID)
File "C:\Python27\lib\site-packages\win32com\gen_py\9BB3CB79 -7098-47D0-83A2-F8
F56D236DC8x0x0x0.py", line 457 in SetApplicationID
return self._oleobj_.InvokeTypes(1610940417, LCID, 1, (24, 0), ((16396, 1),), iAID
File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 516 in __getattr__
raise AttributeError("%s.%s" % (self._username_, attr))
AttributeEror: CATIA.Application.InvokeTypes
It looks like the genpy idea I had was wrong - the objects are the same in both examples.
What should happen is that the line:
self.__objPATECOM = self.objBase(self.objCATIA)
end up calling DispatchBaseClass.__init__ which is in win32com\client\__init__.py. What *should* happen, and appears to be happening in 216 is it should enter the branch:
elif isinstance(oobj, DispatchBaseClass):
try:
oobj = oobj._oleobj_.QueryInterface(self.CLSID, pythoncom.IID_IDispatch) # Must be a valid COM instance
except pythoncom.com_error, details:
import winerror
# Some stupid objects fail here, even tho it is _already_ IDispatch!!??
# Eg, Lotus notes.
# So just let it use the existing object if E_NOINTERFACE
if details.hresult != winerror.E_NOINTERFACE:
raise
oobj = oobj._oleobj_
In the working case it just does the QI so oobj ends up as a "raw" IDispatch. In your failing 217 scenario, it seems to be either not hitting that first condition at all, or enters that block but hits the "except pythoncom.com_error" block.
You might want to instrument that code with prints to try and (a) confirm I'm correct in the working case and (b) work out how it fails in the failing case (ie, whether the 'if' statement simply isn't entered at all or it hits the exception).
I put some print statements into the DispatchBaseClass.__init__ method as you suggested. That method checks whether the oobj == None, then checks whether oobj is an instance of DispatchBaseClass. Both tests fail so the QueryInterface method is never called and the raw oobj is put into the DispatchBaseClass.__dict__
When I printed the raw oobj (that was input to the __init__ method) it printed
<PyIDispatch at 0x000000002371440 with obj at 0x000000000048E258>
If I change the 6th line of the __init__ method from
elif isinstance(oobj, DispatchBaseClass):
to
else:
the code runs without throwing any errors. What does that mean?
What I really don't understand still is why it works in an earlier version, when best I can tell, the types of all the objects and that code block are all the same...