You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
753 lines
30 KiB
753 lines
30 KiB
import sys
|
|
|
|
from comtypes import automation, typeinfo, COMError
|
|
from comtypes.tools import typedesc
|
|
from ctypes import c_void_p, sizeof, alignment
|
|
|
|
try:
|
|
set
|
|
except NameError:
|
|
from sets import Set as set
|
|
|
|
# Is the process 64-bit?
|
|
is_64bits = sys.maxsize > 2**32
|
|
|
|
|
|
################################
|
|
|
|
def PTR(typ):
|
|
return typedesc.PointerType(typ,
|
|
sizeof(c_void_p)*8,
|
|
alignment(c_void_p)*8)
|
|
|
|
# basic C data types, with size and alignment in bits
|
|
char_type = typedesc.FundamentalType("char", 8, 8)
|
|
uchar_type = typedesc.FundamentalType("unsigned char", 8, 8)
|
|
wchar_t_type = typedesc.FundamentalType("wchar_t", 16, 16)
|
|
short_type = typedesc.FundamentalType("short int", 16, 16)
|
|
ushort_type = typedesc.FundamentalType("short unsigned int", 16, 16)
|
|
int_type = typedesc.FundamentalType("int", 32, 32)
|
|
uint_type = typedesc.FundamentalType("unsigned int", 32, 32)
|
|
long_type = typedesc.FundamentalType("long int", 32, 32)
|
|
ulong_type = typedesc.FundamentalType("long unsigned int", 32, 32)
|
|
longlong_type = typedesc.FundamentalType("long long int", 64, 64)
|
|
ulonglong_type = typedesc.FundamentalType("long long unsigned int", 64, 64)
|
|
float_type = typedesc.FundamentalType("float", 32, 32)
|
|
double_type = typedesc.FundamentalType("double", 64, 64)
|
|
|
|
# basic COM data types
|
|
BSTR_type = typedesc.Typedef("BSTR", PTR(wchar_t_type))
|
|
SCODE_type = typedesc.Typedef("SCODE", int_type)
|
|
VARIANT_BOOL_type = typedesc.Typedef("VARIANT_BOOL", short_type)
|
|
HRESULT_type = typedesc.Typedef("HRESULT", ulong_type)
|
|
|
|
VARIANT_type = typedesc.Structure("VARIANT",
|
|
align=alignment(automation.VARIANT)*8,
|
|
members=[], bases=[],
|
|
size=sizeof(automation.VARIANT)*8)
|
|
IDISPATCH_type = typedesc.Typedef("IDispatch", None)
|
|
IUNKNOWN_type = typedesc.Typedef("IUnknown", None)
|
|
DECIMAL_type = typedesc.Structure("DECIMAL",
|
|
align=alignment(automation.DECIMAL)*8,
|
|
members=[], bases=[],
|
|
size=sizeof(automation.DECIMAL)*8)
|
|
|
|
def midlSAFEARRAY(typ):
|
|
return typedesc.SAFEARRAYType(typ)
|
|
|
|
# faked COM data types
|
|
CURRENCY_type = longlong_type # slightly wrong; should be scaled by 10000 - use subclass of longlong?
|
|
DATE_type = double_type # not *that* wrong...
|
|
|
|
COMTYPES = {
|
|
automation.VT_I2: short_type, # 2
|
|
automation.VT_I4: int_type, # 3
|
|
automation.VT_R4: float_type, # 4
|
|
automation.VT_R8: double_type, # 5
|
|
automation.VT_CY: CURRENCY_type, # 6
|
|
automation.VT_DATE: DATE_type, # 7
|
|
automation.VT_BSTR: BSTR_type, # 8
|
|
automation.VT_DISPATCH: PTR(IDISPATCH_type), # 9
|
|
automation.VT_ERROR: SCODE_type, # 10
|
|
automation.VT_BOOL: VARIANT_BOOL_type, # 11
|
|
automation.VT_VARIANT: VARIANT_type, # 12
|
|
automation.VT_UNKNOWN: PTR(IUNKNOWN_type), # 13
|
|
automation.VT_DECIMAL: DECIMAL_type, # 14
|
|
|
|
automation.VT_I1: char_type, # 16
|
|
automation.VT_UI1: uchar_type, # 17
|
|
automation.VT_UI2: ushort_type, # 18
|
|
automation.VT_UI4: ulong_type, # 19
|
|
automation.VT_I8: longlong_type, # 20
|
|
automation.VT_UI8: ulonglong_type, # 21
|
|
automation.VT_INT: int_type, # 22
|
|
automation.VT_UINT: uint_type, # 23
|
|
automation.VT_VOID: typedesc.FundamentalType("void", 0, 0), # 24
|
|
automation.VT_HRESULT: HRESULT_type, # 25
|
|
automation.VT_LPSTR: PTR(char_type), # 30
|
|
automation.VT_LPWSTR: PTR(wchar_t_type), # 31
|
|
}
|
|
|
|
#automation.VT_PTR = 26 # below
|
|
#automation.VT_SAFEARRAY = 27
|
|
#automation.VT_CARRAY = 28 # below
|
|
#automation.VT_USERDEFINED = 29 # below
|
|
|
|
#automation.VT_RECORD = 36
|
|
|
|
#automation.VT_ARRAY = 8192
|
|
#automation.VT_BYREF = 16384
|
|
|
|
################################################################
|
|
|
|
class Parser(object):
|
|
|
|
def make_type(self, tdesc, tinfo):
|
|
try:
|
|
return COMTYPES[tdesc.vt]
|
|
except KeyError:
|
|
pass
|
|
|
|
if tdesc.vt == automation.VT_CARRAY:
|
|
typ = self.make_type(tdesc._.lpadesc[0].tdescElem, tinfo)
|
|
for i in range(tdesc._.lpadesc[0].cDims):
|
|
typ = typedesc.ArrayType(typ,
|
|
tdesc._.lpadesc[0].rgbounds[i].lLbound,
|
|
tdesc._.lpadesc[0].rgbounds[i].cElements-1)
|
|
return typ
|
|
|
|
elif tdesc.vt == automation.VT_PTR:
|
|
typ = self.make_type(tdesc._.lptdesc[0], tinfo)
|
|
return PTR(typ)
|
|
|
|
elif tdesc.vt == automation.VT_USERDEFINED:
|
|
try:
|
|
ti = tinfo.GetRefTypeInfo(tdesc._.hreftype)
|
|
except COMError as details:
|
|
type_name = "__error_hreftype_%d__" % tdesc._.hreftype
|
|
tlib_name = get_tlib_filename(self.tlib)
|
|
if tlib_name is None:
|
|
tlib_name = "unknown typelib"
|
|
message = "\n\tGetRefTypeInfo failed in %s: %s\n\tgenerating type '%s' instead" % \
|
|
(tlib_name, details, type_name)
|
|
import warnings
|
|
warnings.warn(message, UserWarning);
|
|
result = typedesc.Structure(type_name,
|
|
align=8,
|
|
members=[], bases=[],
|
|
size=0)
|
|
return result
|
|
result = self.parse_typeinfo(ti)
|
|
assert result is not None, ti.GetDocumentation(-1)[0]
|
|
return result
|
|
|
|
elif tdesc.vt == automation.VT_SAFEARRAY:
|
|
# SAFEARRAY(<type>), see Don Box pp.331f
|
|
itemtype = self.make_type(tdesc._.lptdesc[0], tinfo)
|
|
return midlSAFEARRAY(itemtype)
|
|
|
|
raise NotImplementedError(tdesc.vt)
|
|
|
|
################################################################
|
|
|
|
# TKIND_ENUM = 0
|
|
def ParseEnum(self, tinfo, ta):
|
|
ta = tinfo.GetTypeAttr()
|
|
enum_name = tinfo.GetDocumentation(-1)[0]
|
|
enum = typedesc.Enumeration(enum_name, 32, 32)
|
|
self._register(enum_name, enum)
|
|
|
|
for i in range(ta.cVars):
|
|
vd = tinfo.GetVarDesc(i)
|
|
name = tinfo.GetDocumentation(vd.memid)[0]
|
|
assert vd.varkind == typeinfo.VAR_CONST
|
|
num_val = vd._.lpvarValue[0].value
|
|
v = typedesc.EnumValue(name, num_val, enum)
|
|
enum.add_value(v)
|
|
return enum
|
|
|
|
# TKIND_RECORD = 1
|
|
def ParseRecord(self, tinfo, ta):
|
|
members = [] # will be filled later
|
|
struct_name, doc, helpcntext, helpfile = tinfo.GetDocumentation(-1)
|
|
struct = typedesc.Structure(struct_name,
|
|
align=ta.cbAlignment*8,
|
|
members=members,
|
|
bases=[],
|
|
size=ta.cbSizeInstance*8)
|
|
self._register(struct_name, struct)
|
|
|
|
tlib, _ = tinfo.GetContainingTypeLib()
|
|
tlib_ta = tlib.GetLibAttr()
|
|
# If this is a 32-bit typlib being loaded in a 64-bit process, then the
|
|
# size and alignment are incorrect. Set the size to None to disable
|
|
# size checks and correct the alignment.
|
|
if is_64bits and tlib_ta.syskind == typeinfo.SYS_WIN32:
|
|
struct.size = None
|
|
struct.align = 64
|
|
|
|
if ta.guid:
|
|
struct._recordinfo_ = (str(tlib_ta.guid),
|
|
tlib_ta.wMajorVerNum, tlib_ta.wMinorVerNum,
|
|
tlib_ta.lcid,
|
|
str(ta.guid))
|
|
|
|
for i in range(ta.cVars):
|
|
vd = tinfo.GetVarDesc(i)
|
|
name = tinfo.GetDocumentation(vd.memid)[0]
|
|
offset = vd._.oInst * 8
|
|
assert vd.varkind == typeinfo.VAR_PERINSTANCE
|
|
typ = self.make_type(vd.elemdescVar.tdesc, tinfo)
|
|
field = typedesc.Field(name,
|
|
typ,
|
|
None, # bits
|
|
offset)
|
|
members.append(field)
|
|
return struct
|
|
|
|
# TKIND_MODULE = 2
|
|
def ParseModule(self, tinfo, ta):
|
|
assert 0 == ta.cImplTypes
|
|
# functions
|
|
for i in range(ta.cFuncs):
|
|
# We skip all function definitions. There are several
|
|
# problems with these, and we can, for comtypes, ignore them.
|
|
continue
|
|
fd = tinfo.GetFuncDesc(i)
|
|
dllname, func_name, ordinal = tinfo.GetDllEntry(fd.memid, fd.invkind)
|
|
func_doc = tinfo.GetDocumentation(fd.memid)[1]
|
|
assert 0 == fd.cParamsOpt # XXX
|
|
returns = self.make_type(fd.elemdescFunc.tdesc, tinfo)
|
|
|
|
if fd.callconv == typeinfo.CC_CDECL:
|
|
attributes = "__cdecl__"
|
|
elif fd.callconv == typeinfo.CC_STDCALL:
|
|
attributes = "__stdcall__"
|
|
else:
|
|
raise ValueError("calling convention %d" % fd.callconv)
|
|
|
|
func = typedesc.Function(func_name, returns, attributes, extern=1)
|
|
if func_doc is not None:
|
|
func.doc = func_doc.encode("mbcs")
|
|
func.dllname = dllname
|
|
self._register(func_name, func)
|
|
for i in range(fd.cParams):
|
|
argtype = self.make_type(fd.lprgelemdescParam[i].tdesc, tinfo)
|
|
func.add_argument(argtype)
|
|
|
|
# constants
|
|
for i in range(ta.cVars):
|
|
vd = tinfo.GetVarDesc(i)
|
|
name, var_doc = tinfo.GetDocumentation(vd.memid)[0:2]
|
|
assert vd.varkind == typeinfo.VAR_CONST
|
|
typ = self.make_type(vd.elemdescVar.tdesc, tinfo)
|
|
var_value = vd._.lpvarValue[0].value
|
|
v = typedesc.Constant(name, typ, var_value)
|
|
self._register(name, v)
|
|
if var_doc is not None:
|
|
v.doc = var_doc
|
|
|
|
# TKIND_INTERFACE = 3
|
|
def ParseInterface(self, tinfo, ta):
|
|
itf_name, itf_doc = tinfo.GetDocumentation(-1)[0:2]
|
|
assert ta.cImplTypes <= 1
|
|
if ta.cImplTypes == 0 and itf_name != "IUnknown":
|
|
# Windows defines an interface IOleControlTypes in ocidl.idl.
|
|
# Don't known what artefact that is - we ignore it.
|
|
# It's an interface without methods anyway.
|
|
if itf_name != "IOleControlTypes":
|
|
message = "Ignoring interface %s which has no base interface" % itf_name
|
|
import warnings
|
|
warnings.warn(message, UserWarning);
|
|
return None
|
|
|
|
itf = typedesc.ComInterface(itf_name,
|
|
members=[],
|
|
base=None,
|
|
iid=str(ta.guid),
|
|
idlflags=self.interface_type_flags(ta.wTypeFlags))
|
|
if itf_doc:
|
|
itf.doc = itf_doc
|
|
self._register(itf_name, itf)
|
|
|
|
if ta.cImplTypes:
|
|
hr = tinfo.GetRefTypeOfImplType(0)
|
|
tibase = tinfo.GetRefTypeInfo(hr)
|
|
itf.base = self.parse_typeinfo(tibase)
|
|
|
|
assert ta.cVars == 0, "vars on an Interface?"
|
|
|
|
members = []
|
|
for i in range(ta.cFuncs):
|
|
fd = tinfo.GetFuncDesc(i)
|
|
## func_name = tinfo.GetDocumentation(fd.memid)[0]
|
|
func_name, func_doc = tinfo.GetDocumentation(fd.memid)[:2]
|
|
assert fd.funckind == typeinfo.FUNC_PUREVIRTUAL
|
|
returns = self.make_type(fd.elemdescFunc.tdesc, tinfo)
|
|
names = tinfo.GetNames(fd.memid, fd.cParams+1)
|
|
names.append("rhs")
|
|
names = names[:fd.cParams + 1]
|
|
assert len(names) == fd.cParams + 1
|
|
flags = self.func_flags(fd.wFuncFlags)
|
|
flags += self.inv_kind(fd.invkind)
|
|
mth = typedesc.ComMethod(fd.invkind, fd.memid, func_name, returns, flags, func_doc)
|
|
mth.oVft = fd.oVft
|
|
for p in range(fd.cParams):
|
|
typ = self.make_type(fd.lprgelemdescParam[p].tdesc, tinfo)
|
|
name = names[p+1]
|
|
flags = fd.lprgelemdescParam[p]._.paramdesc.wParamFlags
|
|
if flags & typeinfo.PARAMFLAG_FHASDEFAULT:
|
|
# XXX should be handled by VARIANT itself
|
|
var = fd.lprgelemdescParam[p]._.paramdesc.pparamdescex[0].varDefaultValue
|
|
default = var.value
|
|
else:
|
|
default = None
|
|
mth.add_argument(typ, name, self.param_flags(flags), default)
|
|
members.append((fd.oVft, mth))
|
|
# Sort the methods by oVft (VTable offset): Some typeinfo
|
|
# don't list methods in VTable order.
|
|
members.sort()
|
|
itf.members.extend([m[1] for m in members])
|
|
|
|
return itf
|
|
|
|
# TKIND_DISPATCH = 4
|
|
def ParseDispatch(self, tinfo, ta):
|
|
itf_name, doc = tinfo.GetDocumentation(-1)[0:2]
|
|
assert ta.cImplTypes == 1
|
|
|
|
hr = tinfo.GetRefTypeOfImplType(0)
|
|
tibase = tinfo.GetRefTypeInfo(hr)
|
|
base = self.parse_typeinfo(tibase)
|
|
members = []
|
|
itf = typedesc.DispInterface(itf_name,
|
|
members=members,
|
|
base=base,
|
|
iid=str(ta.guid),
|
|
idlflags=self.interface_type_flags(ta.wTypeFlags))
|
|
if doc is not None:
|
|
itf.doc = str(doc.split("\0")[0])
|
|
self._register(itf_name, itf)
|
|
|
|
# This code can only handle pure dispinterfaces. Dual
|
|
# interfaces are parsed in ParseInterface().
|
|
assert ta.wTypeFlags & typeinfo.TYPEFLAG_FDUAL == 0
|
|
|
|
for i in range(ta.cVars):
|
|
vd = tinfo.GetVarDesc(i)
|
|
assert vd.varkind == typeinfo.VAR_DISPATCH
|
|
var_name, var_doc = tinfo.GetDocumentation(vd.memid)[0:2]
|
|
typ = self.make_type(vd.elemdescVar.tdesc, tinfo)
|
|
mth = typedesc.DispProperty(vd.memid, var_name, typ, self.var_flags(vd.wVarFlags), var_doc)
|
|
itf.members.append(mth)
|
|
|
|
# At least the EXCEL typelib lists the IUnknown and IDispatch
|
|
# methods even for this kind of interface. I didn't find any
|
|
# indication about these methods in the various flags, so we
|
|
# have to exclude them by name.
|
|
# CLF: 12/14/2012 Do this in a way that does not exclude other methods.
|
|
# I have encountered typlibs where only "QueryInterface", "AddRef"
|
|
# and "Release" are to be skipped.
|
|
ignored_names = set(["QueryInterface", "AddRef", "Release",
|
|
"GetTypeInfoCount", "GetTypeInfo",
|
|
"GetIDsOfNames", "Invoke"])
|
|
|
|
for i in range(ta.cFuncs):
|
|
fd = tinfo.GetFuncDesc(i)
|
|
func_name, func_doc = tinfo.GetDocumentation(fd.memid)[:2]
|
|
if func_name in ignored_names:
|
|
continue
|
|
assert fd.funckind == typeinfo.FUNC_DISPATCH
|
|
|
|
returns = self.make_type(fd.elemdescFunc.tdesc, tinfo)
|
|
names = tinfo.GetNames(fd.memid, fd.cParams+1)
|
|
names.append("rhs")
|
|
names = names[:fd.cParams + 1]
|
|
assert len(names) == fd.cParams + 1 # function name first, then parameter names
|
|
flags = self.func_flags(fd.wFuncFlags)
|
|
flags += self.inv_kind(fd.invkind)
|
|
mth = typedesc.DispMethod(fd.memid, fd.invkind, func_name, returns, flags, func_doc)
|
|
for p in range(fd.cParams):
|
|
typ = self.make_type(fd.lprgelemdescParam[p].tdesc, tinfo)
|
|
name = names[p+1]
|
|
flags = fd.lprgelemdescParam[p]._.paramdesc.wParamFlags
|
|
if flags & typeinfo.PARAMFLAG_FHASDEFAULT:
|
|
var = fd.lprgelemdescParam[p]._.paramdesc.pparamdescex[0].varDefaultValue
|
|
default = var.value
|
|
else:
|
|
default = None
|
|
mth.add_argument(typ, name, self.param_flags(flags), default)
|
|
itf.members.append(mth)
|
|
|
|
return itf
|
|
|
|
def inv_kind(self, invkind):
|
|
NAMES = {automation.DISPATCH_METHOD: [],
|
|
automation.DISPATCH_PROPERTYPUT: ["propput"],
|
|
automation.DISPATCH_PROPERTYPUTREF: ["propputref"],
|
|
automation.DISPATCH_PROPERTYGET: ["propget"]}
|
|
return NAMES[invkind]
|
|
|
|
def func_flags(self, flags):
|
|
# map FUNCFLAGS values to idl attributes
|
|
NAMES = {typeinfo.FUNCFLAG_FRESTRICTED: "restricted",
|
|
typeinfo.FUNCFLAG_FSOURCE: "source",
|
|
typeinfo.FUNCFLAG_FBINDABLE: "bindable",
|
|
typeinfo.FUNCFLAG_FREQUESTEDIT: "requestedit",
|
|
typeinfo.FUNCFLAG_FDISPLAYBIND: "displaybind",
|
|
typeinfo.FUNCFLAG_FDEFAULTBIND: "defaultbind",
|
|
typeinfo.FUNCFLAG_FHIDDEN: "hidden",
|
|
typeinfo.FUNCFLAG_FUSESGETLASTERROR: "usesgetlasterror",
|
|
typeinfo.FUNCFLAG_FDEFAULTCOLLELEM: "defaultcollelem",
|
|
typeinfo.FUNCFLAG_FUIDEFAULT: "uidefault",
|
|
typeinfo.FUNCFLAG_FNONBROWSABLE: "nonbrowsable",
|
|
# typeinfo.FUNCFLAG_FREPLACEABLE: "???",
|
|
typeinfo.FUNCFLAG_FIMMEDIATEBIND: "immediatebind"}
|
|
return [NAMES[bit] for bit in NAMES if bit & flags]
|
|
|
|
def param_flags(self, flags):
|
|
# map PARAMFLAGS values to idl attributes
|
|
NAMES = {typeinfo.PARAMFLAG_FIN: "in",
|
|
typeinfo.PARAMFLAG_FOUT: "out",
|
|
typeinfo.PARAMFLAG_FLCID: "lcid",
|
|
typeinfo.PARAMFLAG_FRETVAL: "retval",
|
|
typeinfo.PARAMFLAG_FOPT: "optional",
|
|
# typeinfo.PARAMFLAG_FHASDEFAULT: "",
|
|
# typeinfo.PARAMFLAG_FHASCUSTDATA: "",
|
|
}
|
|
return [NAMES[bit] for bit in NAMES if bit & flags]
|
|
|
|
def coclass_type_flags(self, flags):
|
|
# map TYPEFLAGS values to idl attributes
|
|
NAMES = {typeinfo.TYPEFLAG_FAPPOBJECT: "appobject",
|
|
# typeinfo.TYPEFLAG_FCANCREATE:
|
|
typeinfo.TYPEFLAG_FLICENSED: "licensed",
|
|
# typeinfo.TYPEFLAG_FPREDECLID:
|
|
typeinfo.TYPEFLAG_FHIDDEN: "hidden",
|
|
typeinfo.TYPEFLAG_FCONTROL: "control",
|
|
typeinfo.TYPEFLAG_FDUAL: "dual",
|
|
typeinfo.TYPEFLAG_FNONEXTENSIBLE: "nonextensible",
|
|
typeinfo.TYPEFLAG_FOLEAUTOMATION: "oleautomation",
|
|
typeinfo.TYPEFLAG_FRESTRICTED: "restricted",
|
|
typeinfo.TYPEFLAG_FAGGREGATABLE: "aggregatable",
|
|
# typeinfo.TYPEFLAG_FREPLACEABLE:
|
|
# typeinfo.TYPEFLAG_FDISPATCHABLE # computed, no flag for this
|
|
typeinfo.TYPEFLAG_FREVERSEBIND: "reversebind",
|
|
typeinfo.TYPEFLAG_FPROXY: "proxy",
|
|
}
|
|
NEGATIVE_NAMES = {typeinfo.TYPEFLAG_FCANCREATE: "noncreatable"}
|
|
return [NAMES[bit] for bit in NAMES if bit & flags] + \
|
|
[NEGATIVE_NAMES[bit] for bit in NEGATIVE_NAMES if not (bit & flags)]
|
|
|
|
def interface_type_flags(self, flags):
|
|
# map TYPEFLAGS values to idl attributes
|
|
NAMES = {typeinfo.TYPEFLAG_FAPPOBJECT: "appobject",
|
|
# typeinfo.TYPEFLAG_FCANCREATE:
|
|
typeinfo.TYPEFLAG_FLICENSED: "licensed",
|
|
# typeinfo.TYPEFLAG_FPREDECLID:
|
|
typeinfo.TYPEFLAG_FHIDDEN: "hidden",
|
|
typeinfo.TYPEFLAG_FCONTROL: "control",
|
|
typeinfo.TYPEFLAG_FDUAL: "dual",
|
|
typeinfo.TYPEFLAG_FNONEXTENSIBLE: "nonextensible",
|
|
typeinfo.TYPEFLAG_FOLEAUTOMATION: "oleautomation",
|
|
typeinfo.TYPEFLAG_FRESTRICTED: "restricted",
|
|
typeinfo.TYPEFLAG_FAGGREGATABLE: "aggregatable",
|
|
# typeinfo.TYPEFLAG_FREPLACEABLE:
|
|
# typeinfo.TYPEFLAG_FDISPATCHABLE # computed, no flag for this
|
|
typeinfo.TYPEFLAG_FREVERSEBIND: "reversebind",
|
|
typeinfo.TYPEFLAG_FPROXY: "proxy",
|
|
}
|
|
NEGATIVE_NAMES = {}
|
|
return [NAMES[bit] for bit in NAMES if bit & flags] + \
|
|
[NEGATIVE_NAMES[bit] for bit in NEGATIVE_NAMES if not (bit & flags)]
|
|
|
|
def var_flags(self, flags):
|
|
NAMES = {typeinfo.VARFLAG_FREADONLY: "readonly",
|
|
typeinfo.VARFLAG_FSOURCE: "source",
|
|
typeinfo.VARFLAG_FBINDABLE: "bindable",
|
|
typeinfo.VARFLAG_FREQUESTEDIT: "requestedit",
|
|
typeinfo.VARFLAG_FDISPLAYBIND: "displaybind",
|
|
typeinfo.VARFLAG_FDEFAULTBIND: "defaultbind",
|
|
typeinfo.VARFLAG_FHIDDEN: "hidden",
|
|
typeinfo.VARFLAG_FRESTRICTED: "restricted",
|
|
typeinfo.VARFLAG_FDEFAULTCOLLELEM: "defaultcollelem",
|
|
typeinfo.VARFLAG_FUIDEFAULT: "uidefault",
|
|
typeinfo.VARFLAG_FNONBROWSABLE: "nonbrowsable",
|
|
typeinfo.VARFLAG_FREPLACEABLE: "replaceable",
|
|
typeinfo.VARFLAG_FIMMEDIATEBIND: "immediatebind"
|
|
}
|
|
return [NAMES[bit] for bit in NAMES if bit & flags]
|
|
|
|
|
|
# TKIND_COCLASS = 5
|
|
def ParseCoClass(self, tinfo, ta):
|
|
# possible ta.wTypeFlags: helpstring, helpcontext, licensed,
|
|
# version, control, hidden, and appobject
|
|
coclass_name, doc = tinfo.GetDocumentation(-1)[0:2]
|
|
tlibattr = tinfo.GetContainingTypeLib()[0].GetLibAttr()
|
|
coclass = typedesc.CoClass(coclass_name,
|
|
str(ta.guid),
|
|
self.coclass_type_flags(ta.wTypeFlags),
|
|
tlibattr)
|
|
if doc is not None:
|
|
coclass.doc = doc
|
|
self._register(coclass_name, coclass)
|
|
|
|
for i in range(ta.cImplTypes):
|
|
hr = tinfo.GetRefTypeOfImplType(i)
|
|
ti = tinfo.GetRefTypeInfo(hr)
|
|
itf = self.parse_typeinfo(ti)
|
|
flags = tinfo.GetImplTypeFlags(i)
|
|
coclass.add_interface(itf, flags)
|
|
return coclass
|
|
|
|
# TKIND_ALIAS = 6
|
|
def ParseAlias(self, tinfo, ta):
|
|
name = tinfo.GetDocumentation(-1)[0]
|
|
typ = self.make_type(ta.tdescAlias, tinfo)
|
|
alias = typedesc.Typedef(name, typ)
|
|
self._register(name, alias)
|
|
return alias
|
|
|
|
# TKIND_UNION = 7
|
|
def ParseUnion(self, tinfo, ta):
|
|
union_name, doc, helpcntext, helpfile = tinfo.GetDocumentation(-1)
|
|
members = []
|
|
union = typedesc.Union(union_name,
|
|
align=ta.cbAlignment*8,
|
|
members=members,
|
|
bases=[],
|
|
size=ta.cbSizeInstance*8)
|
|
self._register(union_name, union)
|
|
|
|
tlib, _ = tinfo.GetContainingTypeLib()
|
|
tlib_ta = tlib.GetLibAttr()
|
|
# If this is a 32-bit typlib being loaded in a 64-bit process, then the
|
|
# size and alignment are incorrect. Set the size to None to disable
|
|
# size checks and correct the alignment.
|
|
if is_64bits and tlib_ta.syskind == typeinfo.SYS_WIN32:
|
|
union.size = None
|
|
union.align = 64
|
|
|
|
for i in range(ta.cVars):
|
|
vd = tinfo.GetVarDesc(i)
|
|
name = tinfo.GetDocumentation(vd.memid)[0]
|
|
offset = vd._.oInst * 8
|
|
assert vd.varkind == typeinfo.VAR_PERINSTANCE
|
|
typ = self.make_type(vd.elemdescVar.tdesc, tinfo)
|
|
field = typedesc.Field(name,
|
|
typ,
|
|
None, # bits
|
|
offset)
|
|
members.append(field)
|
|
return union
|
|
|
|
################################################################
|
|
|
|
def _typelib_module(self, tlib=None):
|
|
if tlib is None:
|
|
tlib = self.tlib
|
|
# return a string that uniquely identifies a typelib.
|
|
# The string doesn't have any meaning outside this instance.
|
|
return str(tlib.GetLibAttr())
|
|
|
|
def _register(self, name, value, tlib=None):
|
|
modname = self._typelib_module(tlib)
|
|
fullname = "%s.%s" % (modname, name)
|
|
if fullname in self.items:
|
|
# XXX Can we really allow this? It happens, at least.
|
|
if isinstance(value, typedesc.External):
|
|
return
|
|
# BUG: We try to register an item that's already registered.
|
|
raise ValueError("Bug: Multiple registered name '%s': %r" % (name, value))
|
|
self.items[fullname] = value
|
|
|
|
def parse_typeinfo(self, tinfo):
|
|
name = tinfo.GetDocumentation(-1)[0]
|
|
modname = self._typelib_module()
|
|
try:
|
|
return self.items["%s.%s" % (modname, name)]
|
|
except KeyError:
|
|
pass
|
|
|
|
tlib = tinfo.GetContainingTypeLib()[0]
|
|
if tlib != self.tlib:
|
|
ta = tinfo.GetTypeAttr()
|
|
size = ta.cbSizeInstance * 8
|
|
align = ta.cbAlignment * 8
|
|
typ = typedesc.External(tlib,
|
|
name,
|
|
size,
|
|
align,
|
|
tlib.GetDocumentation(-1)[:2])
|
|
self._register(name, typ, tlib)
|
|
return typ
|
|
|
|
ta = tinfo.GetTypeAttr()
|
|
tkind = ta.typekind
|
|
|
|
if tkind == typeinfo.TKIND_ENUM: # 0
|
|
return self.ParseEnum(tinfo, ta)
|
|
elif tkind == typeinfo.TKIND_RECORD: # 1
|
|
return self.ParseRecord(tinfo, ta)
|
|
elif tkind == typeinfo.TKIND_MODULE: # 2
|
|
return self.ParseModule(tinfo, ta)
|
|
elif tkind == typeinfo.TKIND_INTERFACE: # 3
|
|
return self.ParseInterface(tinfo, ta)
|
|
elif tkind == typeinfo.TKIND_DISPATCH: # 4
|
|
try:
|
|
# GetRefTypeOfImplType(-1) returns the custom portion
|
|
# of a dispinterface, if it is dual
|
|
href = tinfo.GetRefTypeOfImplType(-1)
|
|
except COMError:
|
|
# no dual interface
|
|
return self.ParseDispatch(tinfo, ta)
|
|
tinfo = tinfo.GetRefTypeInfo(href)
|
|
ta = tinfo.GetTypeAttr()
|
|
assert ta.typekind == typeinfo.TKIND_INTERFACE
|
|
return self.ParseInterface(tinfo, ta)
|
|
elif tkind == typeinfo.TKIND_COCLASS: # 5
|
|
return self.ParseCoClass(tinfo, ta)
|
|
elif tkind == typeinfo.TKIND_ALIAS: # 6
|
|
return self.ParseAlias(tinfo, ta)
|
|
elif tkind == typeinfo.TKIND_UNION: # 7
|
|
return self.ParseUnion(tinfo, ta)
|
|
else:
|
|
print("NYI", tkind)
|
|
## raise "NYI", tkind
|
|
|
|
def parse_LibraryDescription(self):
|
|
la = self.tlib.GetLibAttr()
|
|
name, doc = self.tlib.GetDocumentation(-1)[:2]
|
|
desc = typedesc.TypeLib(name,
|
|
str(la.guid), la.wMajorVerNum, la.wMinorVerNum,
|
|
doc)
|
|
self._register(None, desc)
|
|
|
|
################################################################
|
|
|
|
def parse(self):
|
|
self.parse_LibraryDescription()
|
|
|
|
for i in range(self.tlib.GetTypeInfoCount()):
|
|
tinfo = self.tlib.GetTypeInfo(i)
|
|
self.parse_typeinfo(tinfo)
|
|
return self.items
|
|
|
|
class TlbFileParser(Parser):
|
|
"Parses a type library from a file"
|
|
def __init__(self, path):
|
|
# XXX DOESN'T LOOK CORRECT: We should NOT register the typelib.
|
|
self.tlib = typeinfo.LoadTypeLibEx(path)#, regkind=typeinfo.REGKIND_REGISTER)
|
|
self.items = {}
|
|
|
|
class TypeLibParser(Parser):
|
|
def __init__(self, tlib):
|
|
self.tlib = tlib
|
|
self.items = {}
|
|
|
|
################################################################
|
|
# some interesting typelibs
|
|
|
|
## these do NOT work:
|
|
# XXX infinite loop?
|
|
## path = r"mshtml.tlb" # has propputref
|
|
|
|
# has SAFEARRAY
|
|
# HRESULT Run(BSTR, SAFEARRAY(VARIANT)*, VARIANT*)
|
|
## path = "msscript.ocx"
|
|
|
|
# has SAFEARRAY
|
|
# HRESULT AddAddress(SAFEARRAY(BSTR)*, SAFEARRAY(BSTR)*)
|
|
## path = r"c:\Programme\Microsoft Office\Office\MSWORD8.OLB" # has propputref
|
|
|
|
# has SAFEARRAY:
|
|
# SAFEARRAY(unsigned char) FileSignatureInfo(BSTR, long, MsiSignatureInfo)
|
|
## path = r"msi.dll" # DispProperty
|
|
|
|
# fails packing IDLDESC
|
|
## path = r"C:\Dokumente und Einstellungen\thomas\Desktop\tlb\win.tlb"
|
|
# fails packing WIN32_FIND_DATA
|
|
## path = r"C:\Dokumente und Einstellungen\thomas\Desktop\tlb\win32.tlb"
|
|
# has a POINTER(IUnknown) as default parameter value
|
|
## path = r"c:\Programme\Gemeinsame Dateien\Microsoft Shared\Speech\sapi.dll"
|
|
|
|
|
|
## path = r"hnetcfg.dll"
|
|
## path = r"simpdata.tlb"
|
|
## path = r"nscompat.tlb"
|
|
## path = r"stdole32.tlb"
|
|
|
|
## path = r"shdocvw.dll"
|
|
|
|
## path = r"c:\Programme\Microsoft Office\Office\MSO97.DLL"
|
|
## path = r"PICCLP32.OCX" # DispProperty
|
|
## path = r"MSHFLXGD.OCX" # DispProperty, propputref
|
|
## path = r"scrrun.dll" # propput AND propputref on IDictionary::Item
|
|
## path = r"C:\Dokumente und Einstellungen\thomas\Desktop\tlb\threadapi.tlb"
|
|
|
|
## path = r"..\samples\BITS\bits2_0.tlb"
|
|
|
|
## path = r"c:\vc98\include\activscp.tlb"
|
|
|
|
def get_tlib_filename(tlib):
|
|
# seems if the typelib is not registered, there's no way to
|
|
# determine the filename.
|
|
from ctypes import windll, byref
|
|
from comtypes import BSTR
|
|
la = tlib.GetLibAttr()
|
|
name = BSTR()
|
|
try:
|
|
windll.oleaut32.QueryPathOfRegTypeLib
|
|
except AttributeError:
|
|
# Windows CE doesn't have this function
|
|
return None
|
|
if 0 == windll.oleaut32.QueryPathOfRegTypeLib(byref(la.guid),
|
|
la.wMajorVerNum,
|
|
la.wMinorVerNum,
|
|
0, # lcid
|
|
byref(name)
|
|
):
|
|
return name.value.split("\0")[0]
|
|
return None
|
|
|
|
def _py2exe_hint():
|
|
# If the tlbparser is frozen, we need to include these
|
|
import comtypes.persist
|
|
import comtypes.typeinfo
|
|
import comtypes.automation
|
|
|
|
def generate_module(tlib, ofi, pathname):
|
|
known_symbols = {}
|
|
for name in ("comtypes.persist",
|
|
"comtypes.typeinfo",
|
|
"comtypes.automation",
|
|
"comtypes._others",
|
|
"comtypes",
|
|
"ctypes.wintypes",
|
|
"ctypes"):
|
|
try:
|
|
mod = __import__(name)
|
|
except ImportError:
|
|
if name == "comtypes._others":
|
|
continue
|
|
raise
|
|
for submodule in name.split(".")[1:]:
|
|
mod = getattr(mod, submodule)
|
|
for name in mod.__dict__:
|
|
known_symbols[name] = mod.__name__
|
|
p = TypeLibParser(tlib)
|
|
if pathname is None:
|
|
pathname = get_tlib_filename(tlib)
|
|
items = p.parse()
|
|
|
|
from .codegenerator import Generator
|
|
|
|
gen = Generator(ofi,
|
|
known_symbols=known_symbols,
|
|
)
|
|
|
|
gen.generate_code(list(items.values()), filename=pathname)
|
|
|
|
# -eof-
|