# Code that packs and unpacks the Univgw structures. # See if we have a special directory for the binaries (for developers) import pythoncom from win32com.client import gencache com_error = pythoncom.com_error _univgw = pythoncom._univgw def RegisterInterfaces(typelibGUID, lcid, major, minor, interface_names=None): ret = [] # return a list of (dispid, funcname for our policy's benefit # First see if we have makepy support. If so, we can probably satisfy the request without loading the typelib. try: mod = gencache.GetModuleForTypelib(typelibGUID, lcid, major, minor) except ImportError: mod = None if mod is None: import win32com.client.build # Load up the typelib and build (but don't cache) it now tlb = pythoncom.LoadRegTypeLib(typelibGUID, major, minor, lcid) typecomp_lib = tlb.GetTypeComp() if interface_names is None: interface_names = [] for i in range(tlb.GetTypeInfoCount()): info = tlb.GetTypeInfo(i) doc = tlb.GetDocumentation(i) attr = info.GetTypeAttr() if attr.typekind == pythoncom.TKIND_INTERFACE or ( attr.typekind == pythoncom.TKIND_DISPATCH and attr.wTypeFlags & pythoncom.TYPEFLAG_FDUAL ): interface_names.append(doc[0]) for name in interface_names: type_info, type_comp = typecomp_lib.BindType( name, ) # Not sure why we don't get an exception here - BindType's C # impl looks correct.. if type_info is None: raise ValueError("The interface '%s' can not be located" % (name,)) # If we got back a Dispatch interface, convert to the real interface. attr = type_info.GetTypeAttr() if attr.typekind == pythoncom.TKIND_DISPATCH: refhtype = type_info.GetRefTypeOfImplType(-1) type_info = type_info.GetRefTypeInfo(refhtype) attr = type_info.GetTypeAttr() item = win32com.client.build.VTableItem( type_info, attr, type_info.GetDocumentation(-1) ) _doCreateVTable( item.clsid, item.python_name, item.bIsDispatch, item.vtableFuncs ) for info in item.vtableFuncs: names, dispid, desc = info invkind = desc[4] ret.append((dispid, invkind, names[0])) else: # Cool - can used cached info. if not interface_names: interface_names = list(mod.VTablesToClassMap.values()) for name in interface_names: try: iid = mod.NamesToIIDMap[name] except KeyError: raise ValueError( "Interface '%s' does not exist in this cached typelib" % (name,) ) # print "Processing interface", name sub_mod = gencache.GetModuleForCLSID(iid) is_dispatch = getattr(sub_mod, name + "_vtables_dispatch_", None) method_defs = getattr(sub_mod, name + "_vtables_", None) if is_dispatch is None or method_defs is None: raise ValueError("Interface '%s' is IDispatch only" % (name,)) # And create the univgw defn _doCreateVTable(iid, name, is_dispatch, method_defs) for info in method_defs: names, dispid, desc = info invkind = desc[4] ret.append((dispid, invkind, names[0])) return ret def _doCreateVTable(iid, interface_name, is_dispatch, method_defs): defn = Definition(iid, is_dispatch, method_defs) vtbl = _univgw.CreateVTable(defn, is_dispatch) _univgw.RegisterVTable(vtbl, iid, interface_name) def _CalcTypeSize(typeTuple): t = typeTuple[0] if t & (pythoncom.VT_BYREF | pythoncom.VT_ARRAY): # Its a pointer. cb = _univgw.SizeOfVT(pythoncom.VT_PTR)[1] elif t == pythoncom.VT_RECORD: # Just because a type library uses records doesn't mean the user # is trying to. We need to better place to warn about this, but it # isn't here. # try: # import warnings # warnings.warn("warning: records are known to not work for vtable interfaces") # except ImportError: # print "warning: records are known to not work for vtable interfaces" cb = _univgw.SizeOfVT(pythoncom.VT_PTR)[1] # cb = typeInfo.GetTypeAttr().cbSizeInstance else: cb = _univgw.SizeOfVT(t)[1] return cb class Arg: def __init__(self, arg_info, name=None): self.name = name self.vt, self.inOut, self.default, self.clsid = arg_info self.size = _CalcTypeSize(arg_info) # Offset from the beginning of the arguments of the stack. self.offset = 0 class Method: def __init__(self, method_info, isEventSink=0): all_names, dispid, desc = method_info name = all_names[0] names = all_names[1:] invkind = desc[4] arg_defs = desc[2] ret_def = desc[8] self.dispid = dispid self.invkind = invkind # We dont use this ATM. # self.ret = Arg(ret_def) if isEventSink and name[:2] != "On": name = "On%s" % name self.name = name cbArgs = 0 self.args = [] for argDesc in arg_defs: arg = Arg(argDesc) arg.offset = cbArgs cbArgs = cbArgs + arg.size self.args.append(arg) self.cbArgs = cbArgs self._gw_in_args = self._GenerateInArgTuple() self._gw_out_args = self._GenerateOutArgTuple() def _GenerateInArgTuple(self): # Given a method, generate the in argument tuple l = [] for arg in self.args: if arg.inOut & pythoncom.PARAMFLAG_FIN or arg.inOut == 0: l.append((arg.vt, arg.offset, arg.size)) return tuple(l) def _GenerateOutArgTuple(self): # Given a method, generate the out argument tuple l = [] for arg in self.args: if ( arg.inOut & pythoncom.PARAMFLAG_FOUT or arg.inOut & pythoncom.PARAMFLAG_FRETVAL or arg.inOut == 0 ): l.append((arg.vt, arg.offset, arg.size, arg.clsid)) return tuple(l) class Definition: def __init__(self, iid, is_dispatch, method_defs): self._iid = iid self._methods = [] self._is_dispatch = is_dispatch for info in method_defs: entry = Method(info) self._methods.append(entry) def iid(self): return self._iid def vtbl_argsizes(self): return [m.cbArgs for m in self._methods] def vtbl_argcounts(self): return [len(m.args) for m in self._methods] def dispatch( self, ob, index, argPtr, ReadFromInTuple=_univgw.ReadFromInTuple, WriteFromOutTuple=_univgw.WriteFromOutTuple, ): "Dispatch a call to an interface method." meth = self._methods[index] # Infer S_OK if they don't return anything bizarre. hr = 0 args = ReadFromInTuple(meth._gw_in_args, argPtr) # If ob is a dispatcher, ensure a policy ob = getattr(ob, "policy", ob) # Ensure the correct dispid is setup ob._dispid_to_func_[meth.dispid] = meth.name retVal = ob._InvokeEx_(meth.dispid, 0, meth.invkind, args, None, None) # None is an allowed return value stating that # the code doesn't want to touch any output arguments. if type(retVal) == tuple: # Like pythoncom, we special case a tuple. # However, if they want to return a specific HRESULT, # then they have to return all of the out arguments # AND the HRESULT. if len(retVal) == len(meth._gw_out_args) + 1: hr = retVal[0] retVal = retVal[1:] else: raise TypeError( "Expected %s return values, got: %s" % (len(meth._gw_out_args) + 1, len(retVal)) ) else: retVal = [retVal] retVal.extend([None] * (len(meth._gw_out_args) - 1)) retVal = tuple(retVal) WriteFromOutTuple(retVal, meth._gw_out_args, argPtr) return hr