-
Notifications
You must be signed in to change notification settings - Fork 841
Description
I am using Python 3.8.8 and pywin32 227 on Windows 10. I am trying to do some SAP GUI scripting in Python. All goes well when I don't generate the python modules for the COM interfaces and I rely on dynamic dispatch, however, things break when win32com.client invokes makepy.
A simple example to demonstrates the issue:
import win32com.client
sap = win32com.client.GetObject('SAPGUI')
application = sap.GetScriptingEngine
len(application.Connections)
works. However, after running makepy for the relevant COM module, the same code raises an exception:
File "C:\Users\nicolo01\Documents\Python\examples\sap.py", line 34, in main
len(application.Connections)
TypeError: object of type 'GuiComponentCollection' has no len()
Inspecting the module generated by makepy, the Connection
property is defined like this:
class _Dsapfewse(DispatchBaseClass):
...
_prop_map_get_ = {
...
# Property 'Connections' is an object of type 'GuiComponentCollection'
"Connections": (32900, 2, (13, 0), (), "Connections", '{A464F5B4-CC70-4062-A0A1-6DC9B461D776}'),
...
}
which I think corresponds to this class:
class GuiComponentCollection(CoClassBaseClass): # A CoClass
CLSID = IID('{A464F5B4-CC70-4062-A0A1-6DC9B461D776}')
coclass_sources = [
]
coclass_interfaces = [
ISapCollectionTarget,
]
default_interface = ISapCollectionTarget
and this interface:
class ISapCollectionTarget(DispatchBaseClass):
CLSID = IID('{736C7684-ECDA-4984-87A9-AE358AE8823F}')
coclass_clsid = IID('{A464F5B4-CC70-4062-A0A1-6DC9B461D776}')
# Result is of type GuiComponent
def ElementAt(self, Index=defaultNamedNotOptArg):
ret = self._oleobj_.InvokeTypes(33102, LCID, 1, (13, 0), ((3, 0),),Index
)
if ret is not None:
# See if this IUnknown is really an IDispatch
try:
ret = ret.QueryInterface(pythoncom.IID_IDispatch)
except pythoncom.error:
return ret
ret = Dispatch(ret, 'ElementAt', '{ABCC907C-3AB1-45D9-BF20-D3F647377B06}')
return ret
# Result is of type GuiComponent
def Item(self, Index=defaultNamedNotOptArg):
ret = self._oleobj_.InvokeTypes(0, LCID, 1, (13, 0), ((12, 0),),Index
)
if ret is not None:
# See if this IUnknown is really an IDispatch
try:
ret = ret.QueryInterface(pythoncom.IID_IDispatch)
except pythoncom.error:
return ret
ret = Dispatch(ret, 'Item', '{ABCC907C-3AB1-45D9-BF20-D3F647377B06}')
return ret
_prop_map_get_ = {
"Count": (33100, 2, (3, 0), (), "Count", None),
"Length": (33101, 2, (3, 0), (), "Length", None),
"Type": (32015, 2, (8, 0), (), "Type", None),
"TypeAsNumber": (32032, 2, (3, 0), (), "TypeAsNumber", None),
}
_prop_map_put_ = {
"Count" : ((33100, LCID, 4, 0),()),
"Length" : ((33101, LCID, 4, 0),()),
"NewEnum" : ((-4, LCID, 4, 0),()),
"Type" : ((32015, LCID, 4, 0),()),
"TypeAsNumber" : ((32032, LCID, 4, 0),()),
}
# Default method for this class is 'Item'
def __call__(self, Index=defaultNamedNotOptArg):
ret = self._oleobj_.InvokeTypes(0, LCID, 1, (13, 0), ((12, 0),),Index
)
if ret is not None:
# See if this IUnknown is really an IDispatch
try:
ret = ret.QueryInterface(pythoncom.IID_IDispatch)
except pythoncom.error:
return ret
ret = Dispatch(ret, '__call__', '{ABCC907C-3AB1-45D9-BF20-D3F647377B06}')
return ret
def __str__(self, *args):
return str(self.__call__(*args))
def __int__(self, *args):
return int(self.__call__(*args))
def __iter__(self):
"Return a Python iterator for this object"
try:
ob = self._oleobj_.InvokeTypes(-4,LCID,3,(13, 10),())
except pythoncom.error:
raise TypeError("This object does not support enumeration")
return win32com.client.util.Iterator(ob, '{ABCC907C-3AB1-45D9-BF20-D3F647377B06}')
#This class has Count() property - allow len(ob) to provide this
def __len__(self):
return self._ApplyTypes_(*(33100, 2, (3, 0), (), "Count", None))
#This class has a __len__ - this is needed so 'if object:' always returns TRUE.
def __nonzero__(self):
return True
which seems to implement the right methods and Python interfaces (by the way, it would be cool if __getitem__
could also be implemented for COM interfaces exposing containers). However, I don't really understand how all the pieces are supposed to be glued together, and thus why it does not work as intended. The Connections
property return the right type:
type(application.Connections)
win32com.gen_py.5EA428A0-F2B8-45E7-99FA-0E994E82B5BCx0x1x0.GuiComponentCollection
The same think happens for all other properties that return a COM object. I can go on with dynamic dispatching just fine, without the generated modules, but the generated modules are necessary (or at least they built is triggered by) using event dispatching, thus I am kind of stuck.
Please let me know if there is anything more I can provide to help debug the issue.