Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View file

@ -0,0 +1,53 @@
"""Manages a dictionary of CLSID strings to Python classes.
Primary use of this module is to allow modules generated by
makepy.py to share classes. @makepy@ automatically generates code
which interacts with this module. You should never need to reference
this module directly.
This module only provides support for modules which have been previously
been imported. The gencache module provides some support for loading modules
on demand - once done, this module supports it...
As an example, the MSACCESS.TLB type library makes reference to the
CLSID of the Database object, as defined in DAO3032.DLL. This
allows code using the MSAccess wrapper to natively use Databases.
This obviously applies to all cooperating objects, not just DAO and
Access.
"""
mapCLSIDToClass = {}
def RegisterCLSID( clsid, pythonClass ):
"""Register a class that wraps a CLSID
This function allows a CLSID to be globally associated with a class.
Certain module will automatically convert an IDispatch object to an
instance of the associated class.
"""
mapCLSIDToClass[str(clsid)] = pythonClass
def RegisterCLSIDsFromDict( dict ):
"""Register a dictionary of CLSID's and classes.
This module performs the same function as @RegisterCLSID@, but for
an entire dictionary of associations.
Typically called by makepy generated modules at import time.
"""
mapCLSIDToClass.update(dict)
def GetClass(clsid):
"""Given a CLSID, return the globally associated class.
clsid -- a string CLSID representation to check.
"""
return mapCLSIDToClass[clsid]
def HasClass(clsid):
"""Determines if the CLSID has an associated class.
clsid -- the string CLSID to check
"""
return clsid in mapCLSIDToClass

View file

@ -0,0 +1,547 @@
# This module exists to create the "best" dispatch object for a given
# object. If "makepy" support for a given object is detected, it is
# used, otherwise a dynamic dispatch object.
# Note that if the unknown dispatch object then returns a known
# dispatch object, the known class will be used. This contrasts
# with dynamic.Dispatch behaviour, where dynamic objects are always used.
import pythoncom
from . import dynamic
from . import gencache
import sys
import pywintypes
_PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
def __WrapDispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, \
UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER,
WrapperClass = None):
"""
Helper function to return a makepy generated class for a CLSID if it exists,
otherwise cope by using CDispatch.
"""
assert UnicodeToString is None, "this is deprecated and will go away"
if resultCLSID is None:
try:
typeinfo = dispatch.GetTypeInfo()
if typeinfo is not None: # Some objects return NULL, some raise exceptions...
resultCLSID = str(typeinfo.GetTypeAttr()[0])
except (pythoncom.com_error, AttributeError):
pass
if resultCLSID is not None:
from . import gencache
# Attempt to load generated module support
# This may load the module, and make it available
klass = gencache.GetClassForCLSID(resultCLSID)
if klass is not None:
return klass(dispatch)
# Return a "dynamic" object - best we can do!
if WrapperClass is None: WrapperClass = CDispatch
return dynamic.Dispatch(dispatch, userName, WrapperClass, typeinfo, clsctx=clsctx)
def GetObject(Pathname = None, Class = None, clsctx = None):
"""
Mimic VB's GetObject() function.
ob = GetObject(Class = "ProgID") or GetObject(Class = clsid) will
connect to an already running instance of the COM object.
ob = GetObject(r"c:\blah\blah\foo.xls") (aka the COM moniker syntax)
will return a ready to use Python wrapping of the required COM object.
Note: You must specifiy one or the other of these arguments. I know
this isn't pretty, but it is what VB does. Blech. If you don't
I'll throw ValueError at you. :)
This will most likely throw pythoncom.com_error if anything fails.
"""
if clsctx is None:
clsctx = pythoncom.CLSCTX_ALL
if (Pathname is None and Class is None) or \
(Pathname is not None and Class is not None):
raise ValueError("You must specify a value for Pathname or Class, but not both.")
if Class is not None:
return GetActiveObject(Class, clsctx)
else:
return Moniker(Pathname, clsctx)
def GetActiveObject(Class, clsctx = pythoncom.CLSCTX_ALL):
"""
Python friendly version of GetObject's ProgID/CLSID functionality.
"""
resultCLSID = pywintypes.IID(Class)
dispatch = pythoncom.GetActiveObject(resultCLSID)
dispatch = dispatch.QueryInterface(pythoncom.IID_IDispatch)
return __WrapDispatch(dispatch, Class, resultCLSID = resultCLSID, clsctx = clsctx)
def Moniker(Pathname, clsctx = pythoncom.CLSCTX_ALL):
"""
Python friendly version of GetObject's moniker functionality.
"""
moniker, i, bindCtx = pythoncom.MkParseDisplayName(Pathname)
dispatch = moniker.BindToObject(bindCtx, None, pythoncom.IID_IDispatch)
return __WrapDispatch(dispatch, Pathname, clsctx=clsctx)
def Dispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER):
"""Creates a Dispatch based COM object.
"""
assert UnicodeToString is None, "this is deprecated and will go away"
dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
return __WrapDispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx)
def DispatchEx(clsid, machine=None, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=None, clsctx = None):
"""Creates a Dispatch based COM object on a specific machine.
"""
assert UnicodeToString is None, "this is deprecated and will go away"
# If InProc is registered, DCOM will use it regardless of the machine name
# (and regardless of the DCOM config for the object.) So unless the user
# specifies otherwise, we exclude inproc apps when a remote machine is used.
if clsctx is None:
clsctx = pythoncom.CLSCTX_SERVER
if machine is not None: clsctx = clsctx & ~pythoncom.CLSCTX_INPROC
if machine is None:
serverInfo = None
else:
serverInfo = (machine,)
if userName is None: userName = clsid
dispatch = pythoncom.CoCreateInstanceEx(clsid, None, clsctx, serverInfo, (pythoncom.IID_IDispatch,))[0]
return Dispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx)
class CDispatch(dynamic.CDispatch):
"""
The dynamic class used as a last resort.
The purpose of this overriding of dynamic.CDispatch is to perpetuate the policy
of using the makepy generated wrapper Python class instead of dynamic.CDispatch
if/when possible.
"""
def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
assert UnicodeToString is None, "this is deprecated and will go away"
return Dispatch(ob, userName, returnCLSID,None)
def CastTo(ob, target, typelib = None):
"""'Cast' a COM object to another interface"""
# todo - should support target being an IID
mod = None
if typelib is not None: # caller specified target typelib (TypelibSpec). See e.g. selecttlb.EnumTlbs().
mod = gencache.MakeModuleForTypelib(typelib.clsid, typelib.lcid, int(typelib.major, 16), int(typelib.minor, 16))
if not hasattr(mod, target):
raise ValueError("The interface name '%s' does not appear in the " \
"specified library %r" % (target, typelib.ver_desc))
elif hasattr(target, "index"): # string like
# for now, we assume makepy for this to work.
if "CLSID" not in ob.__class__.__dict__:
# Eeek - no makepy support - try and build it.
ob = gencache.EnsureDispatch(ob)
if "CLSID" not in ob.__class__.__dict__:
raise ValueError("Must be a makepy-able object for this to work")
clsid = ob.CLSID
# Lots of hoops to support "demand-build" - ie, generating
# code for an interface first time it is used. We assume the
# interface name exists in the same library as the object.
# This is generally the case - only referenced typelibs may be
# a problem, and we can handle that later. Maybe <wink>
# So get the generated module for the library itself, then
# find the interface CLSID there.
mod = gencache.GetModuleForCLSID(clsid)
# Get the 'root' module.
mod = gencache.GetModuleForTypelib(mod.CLSID, mod.LCID,
mod.MajorVersion, mod.MinorVersion)
# Find the CLSID of the target
target_clsid = mod.NamesToIIDMap.get(target)
if target_clsid is None:
raise ValueError("The interface name '%s' does not appear in the " \
"same library as object '%r'" % (target, ob))
mod = gencache.GetModuleForCLSID(target_clsid)
if mod is not None:
target_class = getattr(mod, target)
# resolve coclass to interface
target_class = getattr(target_class, "default_interface", target_class)
return target_class(ob) # auto QI magic happens
raise ValueError
class Constants:
"""A container for generated COM constants.
"""
def __init__(self):
self.__dicts__ = [] # A list of dictionaries
def __getattr__(self, a):
for d in self.__dicts__:
if a in d:
return d[a]
raise AttributeError(a)
# And create an instance.
constants = Constants()
# A helpers for DispatchWithEvents - this becomes __setattr__ for the
# temporary class.
def _event_setattr_(self, attr, val):
try:
# Does the COM object have an attribute of this name?
self.__class__.__bases__[0].__setattr__(self, attr, val)
except AttributeError:
# Otherwise just stash it away in the instance.
self.__dict__[attr] = val
# An instance of this "proxy" is created to break the COM circular references
# that exist (ie, when we connect to the COM events, COM keeps a reference
# to the object. Thus, the Event connection must be manually broken before
# our object can die. This solves the problem by manually breaking the connection
# to the real object as the proxy dies.
class EventsProxy:
def __init__(self, ob):
self.__dict__['_obj_'] = ob
def __del__(self):
try:
# If there is a COM error on disconnection we should
# just ignore it - object probably already shut down...
self._obj_.close()
except pythoncom.com_error:
pass
def __getattr__(self, attr):
return getattr(self._obj_, attr)
def __setattr__(self, attr, val):
setattr(self._obj_, attr, val)
def DispatchWithEvents(clsid, user_event_class):
"""Create a COM object that can fire events to a user defined class.
clsid -- The ProgID or CLSID of the object to create.
user_event_class -- A Python class object that responds to the events.
This requires makepy support for the COM object being created. If
this support does not exist it will be automatically generated by
this function. If the object does not support makepy, a TypeError
exception will be raised.
The result is a class instance that both represents the COM object
and handles events from the COM object.
It is important to note that the returned instance is not a direct
instance of the user_event_class, but an instance of a temporary
class object that derives from three classes:
* The makepy generated class for the COM object
* The makepy generated class for the COM events
* The user_event_class as passed to this function.
If this is not suitable, see the getevents function for an alternative
technique of handling events.
Object Lifetimes: Whenever the object returned from this function is
cleaned-up by Python, the events will be disconnected from
the COM object. This is almost always what should happen,
but see the documentation for getevents() for more details.
Example:
>>> class IEEvents:
... def OnVisible(self, visible):
... print "Visible changed:", visible
...
>>> ie = DispatchWithEvents("InternetExplorer.Application", IEEvents)
>>> ie.Visible = 1
Visible changed: 1
>>>
"""
# Create/Get the object.
disp = Dispatch(clsid)
if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
try:
ti = disp._oleobj_.GetTypeInfo()
disp_clsid = ti.GetTypeAttr()[0]
tlb, index = ti.GetContainingTypeLib()
tla = tlb.GetLibAttr()
gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
# Get the class from the module.
disp_class = gencache.GetClassForProgID(str(disp_clsid))
except pythoncom.com_error:
raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
else:
disp_class = disp.__class__
# If the clsid was an object, get the clsid
clsid = disp_class.CLSID
# Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class.
# XXX - we are still "classic style" classes in py2x, so we need can't yet
# use 'type()' everywhere - revisit soon, as py2x will move to new-style too...
try:
from types import ClassType as new_type
except ImportError:
new_type = type # py3k
events_class = getevents(clsid)
if events_class is None:
raise ValueError("This COM object does not support events.")
result_class = new_type("COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__" : _event_setattr_})
instance = result_class(disp._oleobj_) # This only calls the first base class __init__.
events_class.__init__(instance, instance)
if hasattr(user_event_class, "__init__"):
user_event_class.__init__(instance)
return EventsProxy(instance)
def WithEvents(disp, user_event_class):
"""Similar to DispatchWithEvents - except that the returned
object is *not* also usable as the original Dispatch object - that is
the returned object is not dispatchable.
The difference is best summarised by example.
>>> class IEEvents:
... def OnVisible(self, visible):
... print "Visible changed:", visible
...
>>> ie = Dispatch("InternetExplorer.Application")
>>> ie_events = WithEvents(ie, IEEvents)
>>> ie.Visible = 1
Visible changed: 1
Compare with the code sample for DispatchWithEvents, where you get a
single object that is both the interface and the event handler. Note that
the event handler instance will *not* be able to use 'self.' to refer to
IE's methods and properties.
This is mainly useful where using DispatchWithEvents causes
circular reference problems that the simple proxy doesn't deal with
"""
disp = Dispatch(disp)
if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
try:
ti = disp._oleobj_.GetTypeInfo()
disp_clsid = ti.GetTypeAttr()[0]
tlb, index = ti.GetContainingTypeLib()
tla = tlb.GetLibAttr()
gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
# Get the class from the module.
disp_class = gencache.GetClassForProgID(str(disp_clsid))
except pythoncom.com_error:
raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
else:
disp_class = disp.__class__
# Get the clsid
clsid = disp_class.CLSID
# Create a new class that derives from 2 classes - the event sink
# class and the user class.
try:
from types import ClassType as new_type
except ImportError:
new_type = type # py3k
events_class = getevents(clsid)
if events_class is None:
raise ValueError("This COM object does not support events.")
result_class = new_type("COMEventClass", (events_class, user_event_class), {})
instance = result_class(disp) # This only calls the first base class __init__.
if hasattr(user_event_class, "__init__"):
user_event_class.__init__(instance)
return instance
def getevents(clsid):
"""Determine the default outgoing interface for a class, given
either a clsid or progid. It returns a class - you can
conveniently derive your own handler from this class and implement
the appropriate methods.
This method relies on the classes produced by makepy. You must use
either makepy or the gencache module to ensure that the
appropriate support classes have been generated for the com server
that you will be handling events from.
Beware of COM circular references. When the Events class is connected
to the COM object, the COM object itself keeps a reference to the Python
events class. Thus, neither the Events instance or the COM object will
ever die by themselves. The 'close' method on the events instance
must be called to break this chain and allow standard Python collection
rules to manage object lifetimes. Note that DispatchWithEvents() does
work around this problem by the use of a proxy object, but if you use
the getevents() function yourself, you must make your own arrangements
to manage this circular reference issue.
Beware of creating Python circular references: this will happen if your
handler has a reference to an object that has a reference back to
the event source. Call the 'close' method to break the chain.
Example:
>>>win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1)
<module 'win32com.gen_py.....
>>>
>>> class InternetExplorerEvents(win32com.client.getevents("InternetExplorer.Application.1")):
... def OnVisible(self, Visible):
... print "Visibility changed: ", Visible
...
>>>
>>> ie=win32com.client.Dispatch("InternetExplorer.Application.1")
>>> events=InternetExplorerEvents(ie)
>>> ie.Visible=1
Visibility changed: 1
>>>
"""
# find clsid given progid or clsid
clsid=str(pywintypes.IID(clsid))
# return default outgoing interface for that class
klass = gencache.GetClassForCLSID(clsid)
try:
return klass.default_source
except AttributeError:
# See if we have a coclass for the interfaces.
try:
return gencache.GetClassForCLSID(klass.coclass_clsid).default_source
except AttributeError:
return None
# A Record object, as used by the COM struct support
def Record(name, object):
"""Creates a new record object, given the name of the record,
and an object from the same type library.
Example usage would be:
app = win32com.client.Dispatch("Some.Application")
point = win32com.client.Record("SomeAppPoint", app)
point.x = 0
point.y = 0
app.MoveTo(point)
"""
# XXX - to do - probably should allow "object" to already be a module object.
from . import gencache
object = gencache.EnsureDispatch(object)
module = sys.modules[object.__class__.__module__]
# to allow us to work correctly with "demand generated" code,
# we must use the typelib CLSID to obtain the module
# (otherwise we get the sub-module for the object, which
# does not hold the records)
# thus, package may be module, or may be module's parent if demand generated.
package = gencache.GetModuleForTypelib(module.CLSID, module.LCID, module.MajorVersion, module.MinorVersion)
try:
struct_guid = package.RecordMap[name]
except KeyError:
raise ValueError("The structure '%s' is not defined in module '%s'" % (name, package))
return pythoncom.GetRecordFromGuids(module.CLSID, module.MajorVersion, module.MinorVersion, module.LCID, struct_guid)
############################################
# The base of all makepy generated classes
############################################
class DispatchBaseClass:
def __init__(self, oobj=None):
if oobj is None:
oobj = pythoncom.new(self.CLSID)
elif isinstance(oobj, DispatchBaseClass):
try:
oobj = oobj._oleobj_.QueryInterface(self.CLSID, pythoncom.IID_IDispatch) # Must be a valid COM instance
except pythoncom.com_error as 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_
self.__dict__["_oleobj_"] = oobj # so we dont call __setattr__
# Provide a prettier name than the CLSID
def __repr__(self):
# Need to get the docstring for the module for this class.
try:
mod_doc = sys.modules[self.__class__.__module__].__doc__
if mod_doc:
mod_name = "win32com.gen_py." + mod_doc
else:
mod_name = sys.modules[self.__class__.__module__].__name__
except KeyError:
mod_name = "win32com.gen_py.unknown"
return "<%s.%s instance at 0x%s>" % (mod_name, self.__class__.__name__, id(self))
# Delegate comparison to the oleobjs, as they know how to do identity.
def __eq__(self, other):
other = getattr(other, "_oleobj_", other)
return self._oleobj_ == other
def __ne__(self, other):
other = getattr(other, "_oleobj_", other)
return self._oleobj_ != other
def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
return self._get_good_object_(
self._oleobj_.InvokeTypes(dispid, 0, wFlags, retType, argTypes, *args),
user, resultCLSID)
def __getattr__(self, attr):
args=self._prop_map_get_.get(attr)
if args is None:
raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
return self._ApplyTypes_(*args)
def __setattr__(self, attr, value):
if attr in self.__dict__: self.__dict__[attr] = value; return
try:
args, defArgs=self._prop_map_put_[attr]
except KeyError:
raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
self._oleobj_.Invoke(*(args + (value,) + defArgs))
def _get_good_single_object_(self, obj, obUserName=None, resultCLSID=None):
return _get_good_single_object_(obj, obUserName, resultCLSID)
def _get_good_object_(self, obj, obUserName=None, resultCLSID=None):
return _get_good_object_(obj, obUserName, resultCLSID)
# XXX - These should be consolidated with dynamic.py versions.
def _get_good_single_object_(obj, obUserName=None, resultCLSID=None):
if _PyIDispatchType==type(obj):
return Dispatch(obj, obUserName, resultCLSID)
return obj
def _get_good_object_(obj, obUserName=None, resultCLSID=None):
if obj is None:
return None
elif isinstance(obj, tuple):
obUserNameTuple = (obUserName,) * len(obj)
resultCLSIDTuple = (resultCLSID,) * len(obj)
return tuple(map(_get_good_object_, obj, obUserNameTuple, resultCLSIDTuple))
else:
return _get_good_single_object_(obj, obUserName, resultCLSID)
class CoClassBaseClass:
def __init__(self, oobj=None):
if oobj is None: oobj = pythoncom.new(self.CLSID)
self.__dict__["_dispobj_"] = self.default_interface(oobj)
def __repr__(self):
return "<win32com.gen_py.%s.%s>" % (__doc__, self.__class__.__name__)
def __getattr__(self, attr):
d=self.__dict__["_dispobj_"]
if d is not None: return getattr(d, attr)
raise AttributeError(attr)
def __setattr__(self, attr, value):
if attr in self.__dict__: self.__dict__[attr] = value; return
try:
d=self.__dict__["_dispobj_"]
if d is not None:
d.__setattr__(attr, value)
return
except AttributeError:
pass
self.__dict__[attr] = value
# A very simple VARIANT class. Only to be used with poorly-implemented COM
# objects. If an object accepts an arg which is a simple "VARIANT", but still
# is very pickly about the actual variant type (eg, isn't happy with a VT_I4,
# which it would get from a Python integer), you can use this to force a
# particular VT.
class VARIANT(object):
def __init__(self, vt, value):
self.varianttype = vt
self._value = value
# 'value' is a property so when set by pythoncom it gets any magic wrapping
# which normally happens for result objects
def _get_value(self):
return self._value
def _set_value(self, newval):
self._value = _get_good_object_(newval)
def _del_value(self):
del self._value
value = property(_get_value, _set_value, _del_value)
def __repr__(self):
return "win32com.client.VARIANT(%r, %r)" % (self.varianttype, self._value)

View file

@ -0,0 +1,630 @@
"""Contains knowledge to build a COM object definition.
This module is used by both the @dynamic@ and @makepy@ modules to build
all knowledge of a COM object.
This module contains classes which contain the actual knowledge of the object.
This include parameter and return type information, the COM dispid and CLSID, etc.
Other modules may use this information to generate .py files, use the information
dynamically, or possibly even generate .html documentation for objects.
"""
#
# NOTES: DispatchItem and MapEntry used by dynamic.py.
# the rest is used by makepy.py
#
# OleItem, DispatchItem, MapEntry, BuildCallList() is used by makepy
import sys
import string
from keyword import iskeyword
import pythoncom
from pywintypes import TimeType
import winerror
import datetime
# It isn't really clear what the quoting rules are in a C/IDL string and
# literals like a quote char and backslashes makes life a little painful to
# always render the string perfectly - so just punt and fall-back to a repr()
def _makeDocString(s):
if sys.version_info < (3,):
s = s.encode("mbcs")
return repr(s)
error = "PythonCOM.Client.Build error"
class NotSupportedException(Exception): pass # Raised when we cant support a param type.
DropIndirection="DropIndirection"
NoTranslateTypes = [
pythoncom.VT_BOOL, pythoncom.VT_CLSID, pythoncom.VT_CY,
pythoncom.VT_DATE, pythoncom.VT_DECIMAL, pythoncom.VT_EMPTY,
pythoncom.VT_ERROR, pythoncom.VT_FILETIME, pythoncom.VT_HRESULT,
pythoncom.VT_I1, pythoncom.VT_I2, pythoncom.VT_I4,
pythoncom.VT_I8, pythoncom.VT_INT, pythoncom.VT_NULL,
pythoncom.VT_R4, pythoncom.VT_R8, pythoncom.VT_NULL,
pythoncom.VT_STREAM,
pythoncom.VT_UI1, pythoncom.VT_UI2, pythoncom.VT_UI4,
pythoncom.VT_UI8, pythoncom.VT_UINT, pythoncom.VT_VOID,
]
NoTranslateMap = {}
for v in NoTranslateTypes:
NoTranslateMap[v] = None
class MapEntry:
"Simple holder for named attibutes - items in a map."
def __init__(self, desc_or_id, names=None, doc=None, resultCLSID=pythoncom.IID_NULL, resultDoc = None, hidden=0):
if type(desc_or_id)==type(0):
self.dispid = desc_or_id
self.desc = None
else:
self.dispid = desc_or_id[0]
self.desc = desc_or_id
self.names = names
self.doc = doc
self.resultCLSID = resultCLSID
self.resultDocumentation = resultDoc
self.wasProperty = 0 # Have I been transformed into a function so I can pass args?
self.hidden = hidden
def GetResultCLSID(self):
rc = self.resultCLSID
if rc == pythoncom.IID_NULL: return None
return rc
# Return a string, suitable for output - either "'{...}'" or "None"
def GetResultCLSIDStr(self):
rc = self.GetResultCLSID()
if rc is None: return "None"
return repr(str(rc)) # Convert the IID object to a string, then to a string in a string.
def GetResultName(self):
if self.resultDocumentation is None:
return None
return self.resultDocumentation[0]
class OleItem:
typename = "OleItem"
def __init__(self, doc=None):
self.doc = doc
if self.doc:
self.python_name = MakePublicAttributeName(self.doc[0])
else:
self.python_name = None
self.bWritten = 0
self.bIsDispatch = 0
self.bIsSink = 0
self.clsid = None
self.co_class = None
class DispatchItem(OleItem):
typename = "DispatchItem"
def __init__(self, typeinfo=None, attr=None, doc=None, bForUser=1):
OleItem.__init__(self,doc)
self.propMap = {}
self.propMapGet = {}
self.propMapPut = {}
self.mapFuncs = {}
self.defaultDispatchName = None
self.hidden = 0
if typeinfo:
self.Build(typeinfo, attr, bForUser)
def _propMapPutCheck_(self,key,item):
ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
if ins>1: # if a Put property takes more than 1 arg:
if opts+1==ins or ins==item.desc[6]+1:
newKey = "Set" + key
deleteExisting = 0 # This one is still OK
else:
deleteExisting = 1 # No good to us
if key in self.mapFuncs or key in self.propMapGet:
newKey = "Set" + key
else:
newKey = key
item.wasProperty = 1
self.mapFuncs[newKey] = item
if deleteExisting:
del self.propMapPut[key]
def _propMapGetCheck_(self,key,item):
ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
if ins > 0: # if a Get property takes _any_ in args:
if item.desc[6]==ins or ins==opts:
newKey = "Get" + key
deleteExisting = 0 # This one is still OK
else:
deleteExisting = 1 # No good to us
if key in self.mapFuncs:
newKey = "Get" + key
else:
newKey = key
item.wasProperty = 1
self.mapFuncs[newKey] = item
if deleteExisting:
del self.propMapGet[key]
def _AddFunc_(self,typeinfo,fdesc,bForUser):
id = fdesc.memid
funcflags = fdesc.wFuncFlags
try:
names = typeinfo.GetNames(id)
name=names[0]
except pythoncom.ole_error:
name = ""
names = None
doc = None
try:
if bForUser:
doc = typeinfo.GetDocumentation(id)
except pythoncom.ole_error:
pass
if id==0 and name:
self.defaultDispatchName = name
invkind = fdesc.invkind
# We need to translate any Alias', Enums, structs etc in result and args
typerepr, flag, defval = fdesc.rettype
# sys.stderr.write("%s result - %s -> " % (name, typerepr))
typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
# sys.stderr.write("%s\n" % (typerepr,))
fdesc.rettype = typerepr, flag, defval, resultCLSID
# Translate any Alias or Enums in argument list.
argList = []
for argDesc in fdesc.args:
typerepr, flag, defval = argDesc
# sys.stderr.write("%s arg - %s -> " % (name, typerepr))
arg_type, arg_clsid, arg_doc = _ResolveType(typerepr, typeinfo)
argDesc = arg_type, flag, defval, arg_clsid
# sys.stderr.write("%s\n" % (argDesc[0],))
argList.append(argDesc)
fdesc.args = tuple(argList)
hidden = (funcflags & pythoncom.FUNCFLAG_FHIDDEN) != 0
if invkind == pythoncom.INVOKE_PROPERTYGET:
map = self.propMapGet
# This is not the best solution, but I dont think there is
# one without specific "set" syntax.
# If there is a single PUT or PUTREF, it will function as a property.
# If there are both, then the PUT remains a property, and the PUTREF
# gets transformed into a function.
# (in vb, PUT=="obj=other_obj", PUTREF="set obj=other_obj
elif invkind in (pythoncom.INVOKE_PROPERTYPUT, pythoncom.INVOKE_PROPERTYPUTREF):
# Special case
existing = self.propMapPut.get(name, None)
if existing is not None:
if existing.desc[4]==pythoncom.INVOKE_PROPERTYPUT: # Keep this one
map = self.mapFuncs
name = "Set"+name
else: # Existing becomes a func.
existing.wasProperty = 1
self.mapFuncs["Set"+name]=existing
map = self.propMapPut # existing gets overwritten below.
else:
map = self.propMapPut # first time weve seen it.
elif invkind == pythoncom.INVOKE_FUNC:
map = self.mapFuncs
else:
map = None
if not map is None:
# if map.has_key(name):
# sys.stderr.write("Warning - overwriting existing method/attribute %s\n" % name)
map[name] = MapEntry(tuple(fdesc), names, doc, resultCLSID, resultDoc, hidden)
# any methods that can't be reached via DISPATCH we return None
# for, so dynamic dispatch doesnt see it.
if fdesc.funckind != pythoncom.FUNC_DISPATCH:
return None
return (name,map)
return None
def _AddVar_(self,typeinfo,fdesc,bForUser):
### need pythoncom.VARFLAG_FRESTRICTED ...
### then check it
if fdesc.varkind == pythoncom.VAR_DISPATCH:
id = fdesc.memid
names = typeinfo.GetNames(id)
# Translate any Alias or Enums in result.
typerepr, flags, defval = fdesc.elemdescVar
typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
fdesc.elemdescVar = typerepr, flags, defval
doc = None
try:
if bForUser: doc = typeinfo.GetDocumentation(id)
except pythoncom.ole_error:
pass
# handle the enumerator specially
map = self.propMap
# Check if the element is hidden.
hidden = 0
if hasattr(fdesc,"wVarFlags"):
hidden = (fdesc.wVarFlags & 0x40) != 0 # VARFLAG_FHIDDEN
map[names[0]] = MapEntry(tuple(fdesc), names, doc, resultCLSID, resultDoc, hidden)
return (names[0],map)
else:
return None
def Build(self, typeinfo, attr, bForUser = 1):
self.clsid = attr[0]
self.bIsDispatch = (attr.wTypeFlags & pythoncom.TYPEFLAG_FDISPATCHABLE) != 0
if typeinfo is None: return
# Loop over all methods
for j in range(attr[6]):
fdesc = typeinfo.GetFuncDesc(j)
self._AddFunc_(typeinfo,fdesc,bForUser)
# Loop over all variables (ie, properties)
for j in range(attr[7]):
fdesc = typeinfo.GetVarDesc(j)
self._AddVar_(typeinfo,fdesc,bForUser)
# Now post-process the maps. For any "Get" or "Set" properties
# that have arguments, we must turn them into methods. If a method
# of the same name already exists, change the name.
for key, item in list(self.propMapGet.items()):
self._propMapGetCheck_(key,item)
for key, item in list(self.propMapPut.items()):
self._propMapPutCheck_(key,item)
def CountInOutOptArgs(self, argTuple):
"Return tuple counting in/outs/OPTS. Sum of result may not be len(argTuple), as some args may be in/out."
ins = out = opts = 0
for argCheck in argTuple:
inOut = argCheck[1]
if inOut==0:
ins = ins + 1
out = out + 1
else:
if inOut & pythoncom.PARAMFLAG_FIN:
ins = ins + 1
if inOut & pythoncom.PARAMFLAG_FOPT:
opts = opts + 1
if inOut & pythoncom.PARAMFLAG_FOUT:
out = out + 1
return ins, out, opts
def MakeFuncMethod(self, entry, name, bMakeClass = 1):
# If we have a type description, and not varargs...
if entry.desc is not None and (len(entry.desc) < 6 or entry.desc[6]!=-1):
return self.MakeDispatchFuncMethod(entry, name, bMakeClass)
else:
return self.MakeVarArgsFuncMethod(entry, name, bMakeClass)
def MakeDispatchFuncMethod(self, entry, name, bMakeClass = 1):
fdesc = entry.desc
doc = entry.doc
names = entry.names
ret = []
if bMakeClass:
linePrefix = "\t"
defNamedOptArg = "defaultNamedOptArg"
defNamedNotOptArg = "defaultNamedNotOptArg"
defUnnamedArg = "defaultUnnamedArg"
else:
linePrefix = ""
defNamedOptArg = "pythoncom.Missing"
defNamedNotOptArg = "pythoncom.Missing"
defUnnamedArg = "pythoncom.Missing"
defOutArg = "pythoncom.Missing"
id = fdesc[0]
s = linePrefix + 'def ' + name + '(self' + BuildCallList(fdesc, names, defNamedOptArg, defNamedNotOptArg, defUnnamedArg, defOutArg) + '):'
ret.append(s)
if doc and doc[1]:
ret.append(linePrefix + '\t' + _makeDocString(doc[1]))
# print "fdesc is ", fdesc
resclsid = entry.GetResultCLSID()
if resclsid:
resclsid = "'%s'" % resclsid
else:
resclsid = 'None'
# Strip the default values from the arg desc
retDesc = fdesc[8][:2]
argsDesc = tuple([what[:2] for what in fdesc[2]])
# The runtime translation of the return types is expensive, so when we know the
# return type of the function, there is no need to check the type at runtime.
# To qualify, this function must return a "simple" type, and have no byref args.
# Check if we have byrefs or anything in the args which mean we still need a translate.
param_flags = [what[1] for what in fdesc[2]]
bad_params = [flag for flag in param_flags if flag & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FRETVAL)!=0]
s = None
if len(bad_params)==0 and len(retDesc)==2 and retDesc[1]==0:
rd = retDesc[0]
if rd in NoTranslateMap:
s = '%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, argsDesc, _BuildArgList(fdesc, names))
elif rd in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN]:
s = '%s\tret = self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)\n' % (linePrefix, id, fdesc[4], retDesc, repr(argsDesc), _BuildArgList(fdesc, names))
s = s + '%s\tif ret is not None:\n' % (linePrefix,)
if rd == pythoncom.VT_UNKNOWN:
s = s + "%s\t\t# See if this IUnknown is really an IDispatch\n" % (linePrefix,)
s = s + "%s\t\ttry:\n" % (linePrefix,)
s = s + "%s\t\t\tret = ret.QueryInterface(pythoncom.IID_IDispatch)\n" % (linePrefix,)
s = s + "%s\t\texcept pythoncom.error:\n" % (linePrefix,)
s = s + "%s\t\t\treturn ret\n" % (linePrefix,)
s = s + '%s\t\tret = Dispatch(ret, %s, %s)\n' % (linePrefix,repr(name), resclsid)
s = s + '%s\treturn ret' % (linePrefix)
elif rd == pythoncom.VT_BSTR:
s = "%s\t# Result is a Unicode object\n" % (linePrefix,)
s = s + '%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, repr(argsDesc), _BuildArgList(fdesc, names))
# else s remains None
if s is None:
s = '%s\treturn self._ApplyTypes_(%d, %s, %s, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, argsDesc, repr(name), resclsid, _BuildArgList(fdesc, names))
ret.append(s)
ret.append("")
return ret
def MakeVarArgsFuncMethod(self, entry, name, bMakeClass = 1):
fdesc = entry.desc
names = entry.names
doc = entry.doc
ret = []
argPrefix = "self"
if bMakeClass:
linePrefix = "\t"
else:
linePrefix = ""
ret.append(linePrefix + 'def ' + name + '(' + argPrefix + ', *args):')
if doc and doc[1]: ret.append(linePrefix + '\t' + _makeDocString(doc[1]))
if fdesc:
invoketype = fdesc[4]
else:
invoketype = pythoncom.DISPATCH_METHOD
s = linePrefix + '\treturn self._get_good_object_(self._oleobj_.Invoke(*(('
ret.append(s + str(entry.dispid) + ",0,%d,1)+args)),'%s')" % (invoketype, names[0]))
ret.append("")
return ret
# Note - "DispatchItem" poorly named - need a new intermediate class.
class VTableItem(DispatchItem):
def Build(self, typeinfo, attr, bForUser = 1):
DispatchItem.Build(self, typeinfo, attr, bForUser)
assert typeinfo is not None, "Cant build vtables without type info!"
meth_list = list(self.mapFuncs.values()) + list(self.propMapGet.values()) + list(self.propMapPut.values())
meth_list.sort(key=lambda m: m.desc[7])
# Now turn this list into the run-time representation
# (ready for immediate use or writing to gencache)
self.vtableFuncs = []
for entry in meth_list:
self.vtableFuncs.append( (entry.names, entry.dispid, entry.desc) )
# A Lazy dispatch item - builds an item on request using info from
# an ITypeComp. The dynamic module makes the called to build each item,
# and also holds the references to the typeinfo and typecomp.
class LazyDispatchItem(DispatchItem):
typename = "LazyDispatchItem"
def __init__(self, attr, doc):
self.clsid = attr[0]
DispatchItem.__init__(self, None, attr, doc, 0)
typeSubstMap = {
pythoncom.VT_INT: pythoncom.VT_I4,
pythoncom.VT_UINT: pythoncom.VT_UI4,
pythoncom.VT_HRESULT: pythoncom.VT_I4,
}
def _ResolveType(typerepr, itypeinfo):
# Resolve VT_USERDEFINED (often aliases or typed IDispatches)
if type(typerepr)==tuple:
indir_vt, subrepr = typerepr
if indir_vt == pythoncom.VT_PTR:
# If it is a VT_PTR to a VT_USERDEFINED that is an IDispatch/IUnknown,
# then it resolves to simply the object.
# Otherwise, it becomes a ByRef of the resolved type
# We need to drop an indirection level on pointer to user defined interfaces.
# eg, (VT_PTR, (VT_USERDEFINED, somehandle)) needs to become VT_DISPATCH
# only when "somehandle" is an object.
# but (VT_PTR, (VT_USERDEFINED, otherhandle)) doesnt get the indirection dropped.
was_user = type(subrepr)==tuple and subrepr[0]==pythoncom.VT_USERDEFINED
subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
if was_user and subrepr in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN, pythoncom.VT_RECORD]:
# Drop the VT_PTR indirection
return subrepr, sub_clsid, sub_doc
# Change PTR indirection to byref
return subrepr | pythoncom.VT_BYREF, sub_clsid, sub_doc
if indir_vt == pythoncom.VT_SAFEARRAY:
# resolve the array element, and convert to VT_ARRAY
subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
return pythoncom.VT_ARRAY | subrepr, sub_clsid, sub_doc
if indir_vt == pythoncom.VT_CARRAY: # runtime has no support for this yet.
# resolve the array element, and convert to VT_CARRAY
# sheesh - return _something_
return pythoncom.VT_CARRAY, None, None
if indir_vt == pythoncom.VT_USERDEFINED:
try:
resultTypeInfo = itypeinfo.GetRefTypeInfo(subrepr)
except pythoncom.com_error as details:
if details.hresult in [winerror.TYPE_E_CANTLOADLIBRARY, winerror.TYPE_E_LIBNOTREGISTERED]:
# an unregistered interface
return pythoncom.VT_UNKNOWN, None, None
raise
resultAttr = resultTypeInfo.GetTypeAttr()
typeKind = resultAttr.typekind
if typeKind == pythoncom.TKIND_ALIAS:
tdesc = resultAttr.tdescAlias
return _ResolveType(tdesc, resultTypeInfo)
elif typeKind in [pythoncom.TKIND_ENUM, pythoncom.TKIND_MODULE]:
# For now, assume Long
return pythoncom.VT_I4, None, None
elif typeKind == pythoncom.TKIND_DISPATCH:
clsid = resultTypeInfo.GetTypeAttr()[0]
retdoc = resultTypeInfo.GetDocumentation(-1)
return pythoncom.VT_DISPATCH, clsid, retdoc
elif typeKind in [pythoncom.TKIND_INTERFACE,
pythoncom.TKIND_COCLASS]:
# XXX - should probably get default interface for CO_CLASS???
clsid = resultTypeInfo.GetTypeAttr()[0]
retdoc = resultTypeInfo.GetDocumentation(-1)
return pythoncom.VT_UNKNOWN, clsid, retdoc
elif typeKind == pythoncom.TKIND_RECORD:
return pythoncom.VT_RECORD, None, None
raise NotSupportedException("Can not resolve alias or user-defined type")
return typeSubstMap.get(typerepr,typerepr), None, None
def _BuildArgList(fdesc, names):
"Builds list of args to the underlying Invoke method."
# Word has TypeInfo for Insert() method, but says "no args"
numArgs = max(fdesc[6], len(fdesc[2]))
names = list(names)
while None in names:
i = names.index(None)
names[i] = "arg%d" % (i,)
# We've seen 'source safe' libraries offer the name of 'ret' params in
# 'names' - although we can't reproduce this, it would be insane to offer
# more args than we have arg infos for - hence the upper limit on names...
names = list(map(MakePublicAttributeName, names[1:(numArgs + 1)]))
name_num = 0
while len(names) < numArgs:
names.append("arg%d" % (len(names),))
# As per BuildCallList(), avoid huge lines.
# Hack a "\n" at the end of every 5th name - "strides" would be handy
# here but don't exist in 2.2
for i in range(0, len(names), 5):
names[i] = names[i] + "\n\t\t\t"
return "," + ", ".join(names)
valid_identifier_chars = string.ascii_letters + string.digits + "_"
def demunge_leading_underscores(className):
i = 0
while className[i] == "_":
i += 1
assert i >= 2, "Should only be here with names starting with '__'"
return className[i-1:] + className[:i-1]
# Given a "public name" (eg, the name of a class, function, etc)
# make sure it is a legal (and reasonable!) Python name.
def MakePublicAttributeName(className, is_global = False):
# Given a class attribute that needs to be public, convert it to a
# reasonable name.
# Also need to be careful that the munging doesnt
# create duplicates - eg, just removing a leading "_" is likely to cause
# a clash.
# if is_global is True, then the name is a global variable that may
# overwrite a builtin - eg, "None"
if className[:2]=='__':
return demunge_leading_underscores(className)
elif className == 'None':
# assign to None is evil (and SyntaxError in 2.4, even though
# iskeyword says False there) - note that if it was a global
# it would get picked up below
className = 'NONE'
elif iskeyword(className):
# most keywords are lower case (except True, False etc in py3k)
ret = className.capitalize()
# but those which aren't get forced upper.
if ret == className:
ret = ret.upper()
return ret
elif is_global and hasattr(__builtins__, className):
# builtins may be mixed case. If capitalizing it doesn't change it,
# force to all uppercase (eg, "None", "True" become "NONE", "TRUE"
ret = className.capitalize()
if ret==className: # didn't change - force all uppercase.
ret = ret.upper()
return ret
# Strip non printable chars
return ''.join([char for char in className if char in valid_identifier_chars])
# Given a default value passed by a type library, return a string with
# an appropriate repr() for the type.
# Takes a raw ELEMDESC and returns a repr string, or None
# (NOTE: The string itself may be '"None"', which is valid, and different to None.
# XXX - To do: Dates are probably screwed, but can they come in?
def MakeDefaultArgRepr(defArgVal):
try:
inOut = defArgVal[1]
except IndexError:
# something strange - assume is in param.
inOut = pythoncom.PARAMFLAG_FIN
if inOut & pythoncom.PARAMFLAG_FHASDEFAULT:
# times need special handling...
val = defArgVal[2]
if isinstance(val, datetime.datetime):
# VARIANT <-> SYSTEMTIME conversions always lose any sub-second
# resolution, so just use a 'timetuple' here.
return repr(tuple(val.utctimetuple()))
if type(val) is TimeType:
# must be the 'old' pywintypes time object...
year=val.year; month=val.month; day=val.day; hour=val.hour; minute=val.minute; second=val.second; msec=val.msec
return "pywintypes.Time((%(year)d, %(month)d, %(day)d, %(hour)d, %(minute)d, %(second)d,0,0,0,%(msec)d))" % locals()
return repr(val)
return None
def BuildCallList(fdesc, names, defNamedOptArg, defNamedNotOptArg, defUnnamedArg, defOutArg, is_comment = False):
"Builds a Python declaration for a method."
# Names[0] is the func name - param names are from 1.
numArgs = len(fdesc[2])
numOptArgs = fdesc[6]
strval = ''
if numOptArgs==-1: # Special value that says "var args after here"
firstOptArg = numArgs
numArgs = numArgs - 1
else:
firstOptArg = numArgs - numOptArgs
for arg in range(numArgs):
try:
argName = names[arg+1]
namedArg = argName is not None
except IndexError:
namedArg = 0
if not namedArg: argName = "arg%d" % (arg)
thisdesc = fdesc[2][arg]
# See if the IDL specified a default value
defArgVal = MakeDefaultArgRepr(thisdesc)
if defArgVal is None:
# Out params always get their special default
if thisdesc[1] & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FIN) == pythoncom.PARAMFLAG_FOUT:
defArgVal = defOutArg
else:
# Unnamed arg - always allow default values.
if namedArg:
# Is a named argument
if arg >= firstOptArg:
defArgVal = defNamedOptArg
else:
defArgVal = defNamedNotOptArg
else:
defArgVal = defUnnamedArg
argName = MakePublicAttributeName(argName)
# insanely long lines with an 'encoding' flag crashes python 2.4.0
# keep 5 args per line
# This may still fail if the arg names are insane, but that seems
# unlikely. See also _BuildArgList()
if (arg+1) % 5 == 0:
strval = strval + "\n"
if is_comment:
strval = strval + "#"
strval = strval + "\t\t\t"
strval = strval + ", " + argName
if defArgVal:
strval = strval + "=" + defArgVal
if numOptArgs==-1:
strval = strval + ", *" + names[-1]
return strval
if __name__=='__main__':
print("Use 'makepy.py' to generate Python code - this module is just a helper")

View file

@ -0,0 +1,540 @@
"""A utility for browsing COM objects.
Usage:
Command Prompt
Use the command *"python.exe catbrowse.py"*. This will display
display a fairly small, modal dialog.
Pythonwin
Use the "Run Script" menu item, and this will create the browser in an
MDI window. This window can be fully resized.
Details
This module allows browsing of registered Type Libraries, COM categories,
and running COM objects. The display is similar to the Pythonwin object
browser, and displays the objects in a hierarchical window.
Note that this module requires the win32ui (ie, Pythonwin) distribution to
work.
"""
import win32con
import win32api, win32ui
import sys
import pythoncom
from win32com.client import util
from pywin.tools import browser
class HLIRoot(browser.HLIPythonObject):
def __init__(self, title):
self.name = title
def GetSubList(self):
return [HLIHeadingCategory(), HLI_IEnumMoniker(pythoncom.GetRunningObjectTable().EnumRunning(), "Running Objects"), HLIHeadingRegisterdTypeLibs()]
def __cmp__(self, other):
return cmp(self.name, other.name)
class HLICOM(browser.HLIPythonObject):
def GetText(self):
return self.name
def CalculateIsExpandable(self):
return 1
class HLICLSID(HLICOM):
def __init__(self, myobject, name=None ):
if type(myobject)==type(''):
myobject = pythoncom.MakeIID(myobject)
if name is None:
try:
name = pythoncom.ProgIDFromCLSID(myobject)
except pythoncom.com_error:
name = str(myobject)
name = "IID: " + name
HLICOM.__init__(self, myobject, name)
def CalculateIsExpandable(self):
return 0
def GetSubList(self):
return []
class HLI_Interface(HLICOM):
pass
class HLI_Enum(HLI_Interface):
def GetBitmapColumn(self):
return 0 # Always a folder.
def CalculateIsExpandable(self):
if self.myobject is not None:
rc = len(self.myobject.Next(1))>0
self.myobject.Reset()
else:
rc = 0
return rc
pass
class HLI_IEnumMoniker(HLI_Enum):
def GetSubList(self):
ctx = pythoncom.CreateBindCtx()
ret = []
for mon in util.Enumerator(self.myobject):
ret.append(HLI_IMoniker(mon, mon.GetDisplayName(ctx, None)))
return ret
class HLI_IMoniker(HLI_Interface):
def GetSubList(self):
ret = []
ret.append(browser.MakeHLI(self.myobject.Hash(), "Hash Value"))
subenum = self.myobject.Enum(1)
ret.append(HLI_IEnumMoniker(subenum, "Sub Monikers"))
return ret
class HLIHeadingCategory(HLICOM):
"A tree heading for registered categories"
def GetText(self):
return "Registered Categories"
def GetSubList(self):
catinf=pythoncom.CoCreateInstance(pythoncom.CLSID_StdComponentCategoriesMgr,None,pythoncom.CLSCTX_INPROC,pythoncom.IID_ICatInformation)
enum=util.Enumerator(catinf.EnumCategories())
ret = []
try:
for catid, lcid, desc in enum:
ret.append(HLICategory((catid, lcid, desc)))
except pythoncom.com_error:
# Registered categories occasionally seem to give spurious errors.
pass # Use what we already have.
return ret
class HLICategory(HLICOM):
"An actual Registered Category"
def GetText(self):
desc = self.myobject[2]
if not desc: desc = "(unnamed category)"
return desc
def GetSubList(self):
win32ui.DoWaitCursor(1)
catid, lcid, desc = self.myobject
catinf=pythoncom.CoCreateInstance(pythoncom.CLSID_StdComponentCategoriesMgr,None,pythoncom.CLSCTX_INPROC,pythoncom.IID_ICatInformation)
ret = []
for clsid in util.Enumerator(catinf.EnumClassesOfCategories((catid,),())):
ret.append(HLICLSID(clsid))
win32ui.DoWaitCursor(0)
return ret
class HLIHelpFile(HLICOM):
def CalculateIsExpandable(self):
return 0
def GetText(self):
import os
fname, ctx = self.myobject
base = os.path.split(fname)[1]
return "Help reference in %s" %( base)
def TakeDefaultAction(self):
fname, ctx = self.myobject
if ctx:
cmd = win32con.HELP_CONTEXT
else:
cmd = win32con.HELP_FINDER
win32api.WinHelp(win32ui.GetMainFrame().GetSafeHwnd(), fname, cmd, ctx)
def GetBitmapColumn(self):
return 6
class HLIRegisteredTypeLibrary(HLICOM):
def GetSubList(self):
import os
clsidstr, versionStr = self.myobject
collected = []
helpPath = ""
key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "TypeLib\\%s\\%s" % (clsidstr, versionStr))
win32ui.DoWaitCursor(1)
try:
num = 0
while 1:
try:
subKey = win32api.RegEnumKey(key, num)
except win32api.error:
break
hSubKey = win32api.RegOpenKey(key, subKey)
try:
value, typ = win32api.RegQueryValueEx(hSubKey, None)
if typ == win32con.REG_EXPAND_SZ:
value = win32api.ExpandEnvironmentStrings(value)
except win32api.error:
value = ""
if subKey=="HELPDIR":
helpPath = value
elif subKey=="Flags":
flags = value
else:
try:
lcid = int(subKey)
lcidkey = win32api.RegOpenKey(key, subKey)
# Enumerate the platforms
lcidnum = 0
while 1:
try:
platform = win32api.RegEnumKey(lcidkey, lcidnum)
except win32api.error:
break
try:
hplatform = win32api.RegOpenKey(lcidkey, platform)
fname, typ = win32api.RegQueryValueEx(hplatform, None)
if typ == win32con.REG_EXPAND_SZ:
fname = win32api.ExpandEnvironmentStrings(fname)
except win32api.error:
fname = ""
collected.append((lcid, platform, fname))
lcidnum = lcidnum + 1
win32api.RegCloseKey(lcidkey)
except ValueError:
pass
num = num + 1
finally:
win32ui.DoWaitCursor(0)
win32api.RegCloseKey(key)
# Now, loop over my collected objects, adding a TypeLib and a HelpFile
ret = []
# if helpPath: ret.append(browser.MakeHLI(helpPath, "Help Path"))
ret.append(HLICLSID(clsidstr))
for lcid, platform, fname in collected:
extraDescs = []
if platform!="win32":
extraDescs.append(platform)
if lcid:
extraDescs.append("locale=%s"%lcid)
extraDesc = ""
if extraDescs: extraDesc = " (%s)" % ", ".join(extraDescs)
ret.append(HLITypeLib(fname, "Type Library" + extraDesc))
ret.sort()
return ret
class HLITypeLibEntry(HLICOM):
def GetText(self):
tlb, index = self.myobject
name, doc, ctx, helpFile = tlb.GetDocumentation(index)
try:
typedesc = HLITypeKinds[tlb.GetTypeInfoType(index)][1]
except KeyError:
typedesc = "Unknown!"
return name + " - " + typedesc
def GetSubList(self):
tlb, index = self.myobject
name, doc, ctx, helpFile = tlb.GetDocumentation(index)
ret = []
if doc: ret.append(browser.HLIDocString(doc, "Doc"))
if helpFile: ret.append(HLIHelpFile( (helpFile, ctx) ))
return ret
class HLICoClass(HLITypeLibEntry):
def GetSubList(self):
ret = HLITypeLibEntry.GetSubList(self)
tlb, index = self.myobject
typeinfo = tlb.GetTypeInfo(index)
attr = typeinfo.GetTypeAttr()
for j in range(attr[8]):
flags = typeinfo.GetImplTypeFlags(j)
refType = typeinfo.GetRefTypeInfo(typeinfo.GetRefTypeOfImplType(j))
refAttr = refType.GetTypeAttr()
ret.append(browser.MakeHLI(refAttr[0], "Name=%s, Flags = %d" % (refAttr[0], flags)))
return ret
class HLITypeLibMethod(HLITypeLibEntry):
def __init__(self, ob, name = None):
self.entry_type = "Method"
HLITypeLibEntry.__init__(self, ob, name)
def GetSubList(self):
ret = HLITypeLibEntry.GetSubList(self)
tlb, index = self.myobject
typeinfo = tlb.GetTypeInfo(index)
attr = typeinfo.GetTypeAttr()
for i in range(attr[7]):
ret.append(HLITypeLibProperty((typeinfo, i)))
for i in range(attr[6]):
ret.append(HLITypeLibFunction((typeinfo, i)))
return ret
class HLITypeLibEnum(HLITypeLibEntry):
def __init__(self, myitem):
typelib, index = myitem
typeinfo = typelib.GetTypeInfo(index)
self.id = typeinfo.GetVarDesc(index)[0]
name = typeinfo.GetNames(self.id)[0]
HLITypeLibEntry.__init__(self, myitem, name)
def GetText(self):
return self.name + " - Enum/Module"
def GetSubList(self):
ret = []
typelib, index = self.myobject
typeinfo = typelib.GetTypeInfo(index)
attr = typeinfo.GetTypeAttr()
for j in range(attr[7]):
vdesc = typeinfo.GetVarDesc(j)
name = typeinfo.GetNames(vdesc[0])[0]
ret.append(browser.MakeHLI(vdesc[1], name))
return ret
class HLITypeLibProperty(HLICOM):
def __init__(self, myitem):
typeinfo, index = myitem
self.id = typeinfo.GetVarDesc(index)[0]
name = typeinfo.GetNames(self.id)[0]
HLICOM.__init__(self, myitem, name)
def GetText(self):
return self.name + " - Property"
def GetSubList(self):
ret = []
typeinfo, index = self.myobject
names = typeinfo.GetNames(self.id)
if len(names)>1:
ret.append(browser.MakeHLI(names[1:], "Named Params"))
vd = typeinfo.GetVarDesc(index)
ret.append(browser.MakeHLI(self.id, "Dispatch ID"))
ret.append(browser.MakeHLI(vd[1], "Value"))
ret.append(browser.MakeHLI(vd[2], "Elem Desc"))
ret.append(browser.MakeHLI(vd[3], "Var Flags"))
ret.append(browser.MakeHLI(vd[4], "Var Kind"))
return ret
class HLITypeLibFunction(HLICOM):
funckinds = {pythoncom.FUNC_VIRTUAL : "Virtual",
pythoncom.FUNC_PUREVIRTUAL : "Pure Virtual",
pythoncom.FUNC_STATIC : "Static",
pythoncom.FUNC_DISPATCH : "Dispatch",
}
invokekinds = {pythoncom.INVOKE_FUNC: "Function",
pythoncom.INVOKE_PROPERTYGET : "Property Get",
pythoncom.INVOKE_PROPERTYPUT : "Property Put",
pythoncom.INVOKE_PROPERTYPUTREF : "Property Put by reference",
}
funcflags = [(pythoncom.FUNCFLAG_FRESTRICTED, "Restricted"),
(pythoncom.FUNCFLAG_FSOURCE, "Source"),
(pythoncom.FUNCFLAG_FBINDABLE, "Bindable"),
(pythoncom.FUNCFLAG_FREQUESTEDIT, "Request Edit"),
(pythoncom.FUNCFLAG_FDISPLAYBIND, "Display Bind"),
(pythoncom.FUNCFLAG_FDEFAULTBIND, "Default Bind"),
(pythoncom.FUNCFLAG_FHIDDEN, "Hidden"),
(pythoncom.FUNCFLAG_FUSESGETLASTERROR, "Uses GetLastError"),
]
vartypes = {pythoncom.VT_EMPTY: "Empty",
pythoncom.VT_NULL: "NULL",
pythoncom.VT_I2: "Integer 2",
pythoncom.VT_I4: "Integer 4",
pythoncom.VT_R4: "Real 4",
pythoncom.VT_R8: "Real 8",
pythoncom.VT_CY: "CY",
pythoncom.VT_DATE: "Date",
pythoncom.VT_BSTR: "String",
pythoncom.VT_DISPATCH: "IDispatch",
pythoncom.VT_ERROR: "Error",
pythoncom.VT_BOOL: "BOOL",
pythoncom.VT_VARIANT: "Variant",
pythoncom.VT_UNKNOWN: "IUnknown",
pythoncom.VT_DECIMAL: "Decimal",
pythoncom.VT_I1: "Integer 1",
pythoncom.VT_UI1: "Unsigned integer 1",
pythoncom.VT_UI2: "Unsigned integer 2",
pythoncom.VT_UI4: "Unsigned integer 4",
pythoncom.VT_I8: "Integer 8",
pythoncom.VT_UI8: "Unsigned integer 8",
pythoncom.VT_INT: "Integer",
pythoncom.VT_UINT: "Unsigned integer",
pythoncom.VT_VOID: "Void",
pythoncom.VT_HRESULT: "HRESULT",
pythoncom.VT_PTR: "Pointer",
pythoncom.VT_SAFEARRAY: "SafeArray",
pythoncom.VT_CARRAY: "C Array",
pythoncom.VT_USERDEFINED: "User Defined",
pythoncom.VT_LPSTR: "Pointer to string",
pythoncom.VT_LPWSTR: "Pointer to Wide String",
pythoncom.VT_FILETIME: "File time",
pythoncom.VT_BLOB: "Blob",
pythoncom.VT_STREAM: "IStream",
pythoncom.VT_STORAGE: "IStorage",
pythoncom.VT_STORED_OBJECT: "Stored object",
pythoncom.VT_STREAMED_OBJECT: "Streamed object",
pythoncom.VT_BLOB_OBJECT: "Blob object",
pythoncom.VT_CF: "CF",
pythoncom.VT_CLSID: "CLSID",
}
type_flags = [ (pythoncom.VT_VECTOR, "Vector"),
(pythoncom.VT_ARRAY, "Array"),
(pythoncom.VT_BYREF, "ByRef"),
(pythoncom.VT_RESERVED, "Reserved"),
]
def __init__(self, myitem):
typeinfo, index = myitem
self.id = typeinfo.GetFuncDesc(index)[0]
name = typeinfo.GetNames(self.id)[0]
HLICOM.__init__(self, myitem, name)
def GetText(self):
return self.name + " - Function"
def MakeReturnTypeName(self, typ):
justtyp = typ & pythoncom.VT_TYPEMASK
try:
typname = self.vartypes[justtyp]
except KeyError:
typname = "?Bad type?"
for (flag, desc) in self.type_flags:
if flag & typ:
typname = "%s(%s)" % (desc, typname)
return typname
def MakeReturnType(self, returnTypeDesc):
if type(returnTypeDesc)==type(()):
first = returnTypeDesc[0]
result = self.MakeReturnType(first)
if first != pythoncom.VT_USERDEFINED:
result = result + " " + self.MakeReturnType(returnTypeDesc[1])
return result
else:
return self.MakeReturnTypeName(returnTypeDesc)
def GetSubList(self):
ret = []
typeinfo, index = self.myobject
names = typeinfo.GetNames(self.id)
ret.append(browser.MakeHLI(self.id, "Dispatch ID"))
if len(names)>1:
ret.append(browser.MakeHLI(", ".join(names[1:]), "Named Params"))
fd = typeinfo.GetFuncDesc(index)
if fd[1]:
ret.append(browser.MakeHLI(fd[1], "Possible result values"))
if fd[8]:
typ, flags, default = fd[8]
val = self.MakeReturnType(typ)
if flags:
val = "%s (Flags=%d, default=%s)" % (val, flags, default)
ret.append(browser.MakeHLI(val, "Return Type"))
for argDesc in fd[2]:
typ, flags, default = argDesc
val = self.MakeReturnType(typ)
if flags:
val = "%s (Flags=%d)" % (val, flags)
if default is not None:
val = "%s (Default=%s)" % (val, default)
ret.append(browser.MakeHLI(val, "Argument"))
try:
fkind = self.funckinds[fd[3]]
except KeyError:
fkind = "Unknown"
ret.append(browser.MakeHLI(fkind, "Function Kind"))
try:
ikind = self.invokekinds[fd[4]]
except KeyError:
ikind = "Unknown"
ret.append(browser.MakeHLI(ikind, "Invoke Kind"))
# 5 = call conv
# 5 = offset vtbl
ret.append(browser.MakeHLI(fd[6], "Number Optional Params"))
flagDescs = []
for flag, desc in self.funcflags:
if flag & fd[9]:
flagDescs.append(desc)
if flagDescs:
ret.append(browser.MakeHLI(", ".join(flagDescs), "Function Flags"))
return ret
HLITypeKinds = {
pythoncom.TKIND_ENUM : (HLITypeLibEnum, 'Enumeration'),
pythoncom.TKIND_RECORD : (HLITypeLibEntry, 'Record'),
pythoncom.TKIND_MODULE : (HLITypeLibEnum, 'Module'),
pythoncom.TKIND_INTERFACE : (HLITypeLibMethod, 'Interface'),
pythoncom.TKIND_DISPATCH : (HLITypeLibMethod, 'Dispatch'),
pythoncom.TKIND_COCLASS : (HLICoClass, 'CoClass'),
pythoncom.TKIND_ALIAS : (HLITypeLibEntry, 'Alias'),
pythoncom.TKIND_UNION : (HLITypeLibEntry, 'Union')
}
class HLITypeLib(HLICOM):
def GetSubList(self):
ret = []
ret.append(browser.MakeHLI(self.myobject, "Filename"))
try:
tlb = pythoncom.LoadTypeLib(self.myobject)
except pythoncom.com_error:
return [browser.MakeHLI("%s can not be loaded" % self.myobject)]
for i in range(tlb.GetTypeInfoCount()):
try:
ret.append(HLITypeKinds[tlb.GetTypeInfoType(i)][0]( (tlb, i) ) )
except pythoncom.com_error:
ret.append(browser.MakeHLI("The type info can not be loaded!"))
ret.sort()
return ret
class HLIHeadingRegisterdTypeLibs(HLICOM):
"A tree heading for registered type libraries"
def GetText(self):
return "Registered Type Libraries"
def GetSubList(self):
# Explicit lookup in the registry.
ret = []
key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "TypeLib")
win32ui.DoWaitCursor(1)
try:
num = 0
while 1:
try:
keyName = win32api.RegEnumKey(key, num)
except win32api.error:
break
# Enumerate all version info
subKey = win32api.RegOpenKey(key, keyName)
name = None
try:
subNum = 0
bestVersion = 0.0
while 1:
try:
versionStr = win32api.RegEnumKey(subKey, subNum)
except win32api.error:
break
try:
versionFlt = float(versionStr)
except ValueError:
versionFlt = 0 # ????
if versionFlt > bestVersion:
bestVersion = versionFlt
name = win32api.RegQueryValue(subKey, versionStr)
subNum = subNum + 1
finally:
win32api.RegCloseKey(subKey)
if name is not None:
ret.append(HLIRegisteredTypeLibrary((keyName, versionStr), name))
num = num + 1
finally:
win32api.RegCloseKey(key)
win32ui.DoWaitCursor(0)
ret.sort()
return ret
def main():
from pywin.tools import hierlist
root = HLIRoot("COM Browser")
if "app" in sys.modules:
# do it in a window
browser.MakeTemplate()
browser.template.OpenObject(root)
else:
# list=hierlist.HierListWithItems( root, win32ui.IDB_BROWSER_HIER )
# dlg=hierlist.HierDialog("COM Browser",list)
dlg = browser.dynamic_browser(root)
dlg.DoModal()
if __name__=='__main__':
main()
ni = pythoncom._GetInterfaceCount()
ng = pythoncom._GetGatewayCount()
if ni or ng:
print("Warning - exiting with %d/%d objects alive" % (ni,ng))

View file

@ -0,0 +1,43 @@
"""Utilities for working with Connections"""
import win32com.server.util, pythoncom
class SimpleConnection:
"A simple, single connection object"
def __init__(self, coInstance = None, eventInstance = None, eventCLSID = None, debug = 0):
self.cp = None
self.cookie = None
self.debug = debug
if not coInstance is None:
self.Connect(coInstance , eventInstance, eventCLSID)
def __del__(self):
try:
self.Disconnect()
except pythoncom.error:
# Ignore disconnection as we are torn down.
pass
def _wrap(self, obj):
useDispatcher = None
if self.debug:
from win32com.server import dispatcher
useDispatcher = dispatcher.DefaultDebugDispatcher
return win32com.server.util.wrap(obj, useDispatcher=useDispatcher)
def Connect(self, coInstance, eventInstance, eventCLSID = None):
try:
oleobj = coInstance._oleobj_
except AttributeError:
oleobj = coInstance
cpc=oleobj.QueryInterface(pythoncom.IID_IConnectionPointContainer)
if eventCLSID is None: eventCLSID = eventInstance.CLSID
comEventInstance = self._wrap(eventInstance)
self.cp=cpc.FindConnectionPoint(eventCLSID)
self.cookie = self.cp.Advise(comEventInstance)
def Disconnect(self):
if not self.cp is None:
if self.cookie:
self.cp.Unadvise(self.cookie)
self.cookie = None
self.cp = None

View file

@ -0,0 +1,581 @@
"""Support for dynamic COM client support.
Introduction
Dynamic COM client support is the ability to use a COM server without
prior knowledge of the server. This can be used to talk to almost all
COM servers, including much of MS Office.
In general, you should not use this module directly - see below.
Example
>>> import win32com.client
>>> xl = win32com.client.Dispatch("Excel.Application")
# The line above invokes the functionality of this class.
# xl is now an object we can use to talk to Excel.
>>> xl.Visible = 1 # The Excel window becomes visible.
"""
import sys
import traceback
import types
import pythoncom
import winerror
from . import build
from pywintypes import IIDType
import win32com.client # Needed as code we eval() references it.
debugging=0 # General debugging
debugging_attr=0 # Debugging dynamic attribute lookups.
LCID = 0x0
# These errors generally mean the property or method exists,
# but can't be used in this context - eg, property instead of a method, etc.
# Used to determine if we have a real error or not.
ERRORS_BAD_CONTEXT = [
winerror.DISP_E_MEMBERNOTFOUND,
winerror.DISP_E_BADPARAMCOUNT,
winerror.DISP_E_PARAMNOTOPTIONAL,
winerror.DISP_E_TYPEMISMATCH,
winerror.E_INVALIDARG,
]
ALL_INVOKE_TYPES = [
pythoncom.INVOKE_PROPERTYGET,
pythoncom.INVOKE_PROPERTYPUT,
pythoncom.INVOKE_PROPERTYPUTREF,
pythoncom.INVOKE_FUNC
]
def debug_print(*args):
if debugging:
for arg in args:
print(arg, end=' ')
print()
def debug_attr_print(*args):
if debugging_attr:
for arg in args:
print(arg, end=' ')
print()
# A helper to create method objects on the fly
py3k = sys.version_info > (3,0)
if py3k:
def MakeMethod(func, inst, cls):
return types.MethodType(func, inst) # class not needed in py3k
else:
MakeMethod = types.MethodType # all args used in py2k.
# get the type objects for IDispatch and IUnknown
PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
PyIUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]
if py3k:
_GoodDispatchTypes=(str, IIDType)
else:
_GoodDispatchTypes=(str, IIDType, str)
_defaultDispatchItem=build.DispatchItem
def _GetGoodDispatch(IDispatch, clsctx = pythoncom.CLSCTX_SERVER):
# quick return for most common case
if isinstance(IDispatch, PyIDispatchType):
return IDispatch
if isinstance(IDispatch, _GoodDispatchTypes):
try:
IDispatch = pythoncom.connect(IDispatch)
except pythoncom.ole_error:
IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
else:
# may already be a wrapped class.
IDispatch = getattr(IDispatch, "_oleobj_", IDispatch)
return IDispatch
def _GetGoodDispatchAndUserName(IDispatch, userName, clsctx):
# Get a dispatch object, and a 'user name' (ie, the name as
# displayed to the user in repr() etc.
if userName is None:
# Displayed name should be a plain string in py2k, and unicode in py3k
if isinstance(IDispatch, str):
userName = IDispatch
elif not py3k and isinstance(IDispatch, str):
# 2to3 converts the above 'unicode' to 'str', but this will never be executed in py3k
userName = IDispatch.encode("ascii", "replace")
## ??? else userName remains None ???
elif not py3k and isinstance(userName, str):
# 2to3 converts the above 'unicode' to 'str', but this will never be executed in py3k
# As above - always a plain string in py2k
userName = userName.encode("ascii", "replace")
else:
userName = str(userName)
return (_GetGoodDispatch(IDispatch, clsctx), userName)
def _GetDescInvokeType(entry, invoke_type):
# determine the wFlags argument passed as input to IDispatch::Invoke
if not entry or not entry.desc: return invoke_type
varkind = entry.desc[4] # from VARDESC struct returned by ITypeComp::Bind
if varkind == pythoncom.VAR_DISPATCH and invoke_type == pythoncom.INVOKE_PROPERTYGET:
return pythoncom.INVOKE_FUNC | invoke_type # DISPATCH_METHOD & DISPATCH_PROPERTYGET can be combined in IDispatch::Invoke
else:
return varkind
def Dispatch(IDispatch, userName = None, createClass = None, typeinfo = None, UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER):
assert UnicodeToString is None, "this is deprecated and will go away"
IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch,userName,clsctx)
if createClass is None:
createClass = CDispatch
lazydata = None
try:
if typeinfo is None:
typeinfo = IDispatch.GetTypeInfo()
if typeinfo is not None:
try:
#try for a typecomp
typecomp = typeinfo.GetTypeComp()
lazydata = typeinfo, typecomp
except pythoncom.com_error:
pass
except pythoncom.com_error:
typeinfo = None
olerepr = MakeOleRepr(IDispatch, typeinfo, lazydata)
return createClass(IDispatch, olerepr, userName, lazydata=lazydata)
def MakeOleRepr(IDispatch, typeinfo, typecomp):
olerepr = None
if typeinfo is not None:
try:
attr = typeinfo.GetTypeAttr()
# If the type info is a special DUAL interface, magically turn it into
# a DISPATCH typeinfo.
if attr[5] == pythoncom.TKIND_INTERFACE and attr[11] & pythoncom.TYPEFLAG_FDUAL:
# Get corresponding Disp interface;
# -1 is a special value which does this for us.
href = typeinfo.GetRefTypeOfImplType(-1);
typeinfo = typeinfo.GetRefTypeInfo(href)
attr = typeinfo.GetTypeAttr()
if typecomp is None:
olerepr = build.DispatchItem(typeinfo, attr, None, 0)
else:
olerepr = build.LazyDispatchItem(attr, None)
except pythoncom.ole_error:
pass
if olerepr is None: olerepr = build.DispatchItem()
return olerepr
def DumbDispatch(IDispatch, userName = None, createClass = None,UnicodeToString=None, clsctx=pythoncom.CLSCTX_SERVER):
"Dispatch with no type info"
assert UnicodeToString is None, "this is deprecated and will go away"
IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch,userName,clsctx)
if createClass is None:
createClass = CDispatch
return createClass(IDispatch, build.DispatchItem(), userName)
class CDispatch:
def __init__(self, IDispatch, olerepr, userName=None, UnicodeToString=None, lazydata=None):
assert UnicodeToString is None, "this is deprecated and will go away"
if userName is None: userName = "<unknown>"
self.__dict__['_oleobj_'] = IDispatch
self.__dict__['_username_'] = userName
self.__dict__['_olerepr_'] = olerepr
self.__dict__['_mapCachedItems_'] = {}
self.__dict__['_builtMethods_'] = {}
self.__dict__['_enum_'] = None
self.__dict__['_unicode_to_string_'] = None
self.__dict__['_lazydata_'] = lazydata
def __call__(self, *args):
"Provide 'default dispatch' COM functionality - allow instance to be called"
if self._olerepr_.defaultDispatchName:
invkind, dispid = self._find_dispatch_type_(self._olerepr_.defaultDispatchName)
else:
invkind, dispid = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, pythoncom.DISPID_VALUE
if invkind is not None:
allArgs = (dispid,LCID,invkind,1) + args
return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
raise TypeError("This dispatch object does not define a default method")
def __bool__(self):
return True # ie "if object:" should always be "true" - without this, __len__ is tried.
# _Possibly_ want to defer to __len__ if available, but Im not sure this is
# desirable???
def __repr__(self):
return "<COMObject %s>" % (self._username_)
def __str__(self):
# __str__ is used when the user does "print object", so we gracefully
# fall back to the __repr__ if the object has no default method.
try:
return str(self.__call__())
except pythoncom.com_error as details:
if details.hresult not in ERRORS_BAD_CONTEXT:
raise
return self.__repr__()
# Delegate comparison to the oleobjs, as they know how to do identity.
def __eq__(self, other):
other = getattr(other, "_oleobj_", other)
return self._oleobj_ == other
def __ne__(self, other):
other = getattr(other, "_oleobj_", other)
return self._oleobj_ != other
def __int__(self):
return int(self.__call__())
def __len__(self):
invkind, dispid = self._find_dispatch_type_("Count")
if invkind:
return self._oleobj_.Invoke(dispid, LCID, invkind, 1)
raise TypeError("This dispatch object does not define a Count method")
def _NewEnum(self):
try:
invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
enum = self._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,LCID,invkind,(13, 10),())
except pythoncom.com_error:
return None # no enumerator for this object.
from . import util
return util.WrapEnum(enum, None)
def __getitem__(self, index): # syver modified
# Improved __getitem__ courtesy Syver Enstad
# Must check _NewEnum before Item, to ensure b/w compat.
if isinstance(index, int):
if self.__dict__['_enum_'] is None:
self.__dict__['_enum_'] = self._NewEnum()
if self.__dict__['_enum_'] is not None:
return self._get_good_object_(self._enum_.__getitem__(index))
# See if we have an "Item" method/property we can use (goes hand in hand with Count() above!)
invkind, dispid = self._find_dispatch_type_("Item")
if invkind is not None:
return self._get_good_object_(self._oleobj_.Invoke(dispid, LCID, invkind, 1, index))
raise TypeError("This object does not support enumeration")
def __setitem__(self, index, *args):
# XXX - todo - We should support calling Item() here too!
# print "__setitem__ with", index, args
if self._olerepr_.defaultDispatchName:
invkind, dispid = self._find_dispatch_type_(self._olerepr_.defaultDispatchName)
else:
invkind, dispid = pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF, pythoncom.DISPID_VALUE
if invkind is not None:
allArgs = (dispid,LCID,invkind,0,index) + args
return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
raise TypeError("This dispatch object does not define a default method")
def _find_dispatch_type_(self, methodName):
if methodName in self._olerepr_.mapFuncs:
item = self._olerepr_.mapFuncs[methodName]
return item.desc[4], item.dispid
if methodName in self._olerepr_.propMapGet:
item = self._olerepr_.propMapGet[methodName]
return item.desc[4], item.dispid
try:
dispid = self._oleobj_.GetIDsOfNames(0,methodName)
except: ### what error?
return None, None
return pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, dispid
def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
return self._get_good_object_(result, user, resultCLSID)
def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
# Given a dispatch object, wrap it in a class
assert UnicodeToString is None, "this is deprecated and will go away"
return Dispatch(ob, userName)
def _get_good_single_object_(self,ob,userName = None, ReturnCLSID=None):
if isinstance(ob, PyIDispatchType):
# make a new instance of (probably this) class.
return self._wrap_dispatch_(ob, userName, ReturnCLSID)
if isinstance(ob, PyIUnknownType):
try:
ob = ob.QueryInterface(pythoncom.IID_IDispatch)
except pythoncom.com_error:
# It is an IUnknown, but not an IDispatch, so just let it through.
return ob
return self._wrap_dispatch_(ob, userName, ReturnCLSID)
return ob
def _get_good_object_(self,ob,userName = None, ReturnCLSID=None):
"""Given an object (usually the retval from a method), make it a good object to return.
Basically checks if it is a COM object, and wraps it up.
Also handles the fact that a retval may be a tuple of retvals"""
if ob is None: # Quick exit!
return None
elif isinstance(ob, tuple):
return tuple(map(lambda o, s=self, oun=userName, rc=ReturnCLSID: s._get_good_single_object_(o, oun, rc), ob))
else:
return self._get_good_single_object_(ob)
def _make_method_(self, name):
"Make a method object - Assumes in olerepr funcmap"
methodName = build.MakePublicAttributeName(name) # translate keywords etc.
methodCodeList = self._olerepr_.MakeFuncMethod(self._olerepr_.mapFuncs[name], methodName,0)
methodCode = "\n".join(methodCodeList)
try:
# print "Method code for %s is:\n" % self._username_, methodCode
# self._print_details_()
codeObject = compile(methodCode, "<COMObject %s>" % self._username_,"exec")
# Exec the code object
tempNameSpace = {}
# "Dispatch" in the exec'd code is win32com.client.Dispatch, not ours.
globNameSpace = globals().copy()
globNameSpace["Dispatch"] = win32com.client.Dispatch
exec(codeObject, globNameSpace, tempNameSpace) # self.__dict__, self.__dict__
name = methodName
# Save the function in map.
fn = self._builtMethods_[name] = tempNameSpace[name]
newMeth = MakeMethod(fn, self, self.__class__)
return newMeth
except:
debug_print("Error building OLE definition for code ", methodCode)
traceback.print_exc()
return None
def _Release_(self):
"""Cleanup object - like a close - to force cleanup when you dont
want to rely on Python's reference counting."""
for childCont in self._mapCachedItems_.values():
childCont._Release_()
self._mapCachedItems_ = {}
if self._oleobj_:
self._oleobj_.Release()
self.__dict__['_oleobj_'] = None
if self._olerepr_:
self.__dict__['_olerepr_'] = None
self._enum_ = None
def _proc_(self, name, *args):
"""Call the named method as a procedure, rather than function.
Mainly used by Word.Basic, which whinges about such things."""
try:
item = self._olerepr_.mapFuncs[name]
dispId = item.dispid
return self._get_good_object_(self._oleobj_.Invoke(*(dispId, LCID, item.desc[4], 0) + (args) ))
except KeyError:
raise AttributeError(name)
def _print_details_(self):
"Debug routine - dumps what it knows about an object."
print("AxDispatch container",self._username_)
try:
print("Methods:")
for method in self._olerepr_.mapFuncs.keys():
print("\t", method)
print("Props:")
for prop, entry in self._olerepr_.propMap.items():
print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
print("Get Props:")
for prop, entry in self._olerepr_.propMapGet.items():
print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
print("Put Props:")
for prop, entry in self._olerepr_.propMapPut.items():
print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
except:
traceback.print_exc()
def __LazyMap__(self, attr):
try:
if self._LazyAddAttr_(attr):
debug_attr_print("%s.__LazyMap__(%s) added something" % (self._username_,attr))
return 1
except AttributeError:
return 0
# Using the typecomp, lazily create a new attribute definition.
def _LazyAddAttr_(self,attr):
if self._lazydata_ is None: return 0
res = 0
typeinfo, typecomp = self._lazydata_
olerepr = self._olerepr_
# We need to explicitly check each invoke type individually - simply
# specifying '0' will bind to "any member", which may not be the one
# we are actually after (ie, we may be after prop_get, but returned
# the info for the prop_put.)
for i in ALL_INVOKE_TYPES:
try:
x,t = typecomp.Bind(attr,i)
# Support 'Get' and 'Set' properties - see
# bug 1587023
if x==0 and attr[:3] in ('Set', 'Get'):
x,t = typecomp.Bind(attr[3:], i)
if x==1: #it's a FUNCDESC
r = olerepr._AddFunc_(typeinfo,t,0)
elif x==2: #it's a VARDESC
r = olerepr._AddVar_(typeinfo,t,0)
else: #not found or TYPEDESC/IMPLICITAPP
r=None
if not r is None:
key, map = r[0],r[1]
item = map[key]
if map==olerepr.propMapPut:
olerepr._propMapPutCheck_(key,item)
elif map==olerepr.propMapGet:
olerepr._propMapGetCheck_(key,item)
res = 1
except:
pass
return res
def _FlagAsMethod(self, *methodNames):
"""Flag these attribute names as being methods.
Some objects do not correctly differentiate methods and
properties, leading to problems when calling these methods.
Specifically, trying to say: ob.SomeFunc()
may yield an exception "None object is not callable"
In this case, an attempt to fetch the *property*has worked
and returned None, rather than indicating it is really a method.
Calling: ob._FlagAsMethod("SomeFunc")
should then allow this to work.
"""
for name in methodNames:
details = build.MapEntry(self.__AttrToID__(name), (name,))
self._olerepr_.mapFuncs[name] = details
def __AttrToID__(self,attr):
debug_attr_print("Calling GetIDsOfNames for property %s in Dispatch container %s" % (attr, self._username_))
return self._oleobj_.GetIDsOfNames(0,attr)
def __getattr__(self, attr):
if attr=='__iter__':
# We can't handle this as a normal method, as if the attribute
# exists, then it must return an iterable object.
try:
invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
enum = self._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,LCID,invkind,(13, 10),())
except pythoncom.com_error:
raise AttributeError("This object can not function as an iterator")
# We must return a callable object.
class Factory:
def __init__(self, ob):
self.ob = ob
def __call__(self):
import win32com.client.util
return win32com.client.util.Iterator(self.ob)
return Factory(enum)
if attr.startswith('_') and attr.endswith('_'): # Fast-track.
raise AttributeError(attr)
# If a known method, create new instance and return.
try:
return MakeMethod(self._builtMethods_[attr], self, self.__class__)
except KeyError:
pass
# XXX - Note that we current are case sensitive in the method.
#debug_attr_print("GetAttr called for %s on DispatchContainer %s" % (attr,self._username_))
# First check if it is in the method map. Note that an actual method
# must not yet exist, (otherwise we would not be here). This
# means we create the actual method object - which also means
# this code will never be asked for that method name again.
if attr in self._olerepr_.mapFuncs:
return self._make_method_(attr)
# Delegate to property maps/cached items
retEntry = None
if self._olerepr_ and self._oleobj_:
# first check general property map, then specific "put" map.
retEntry = self._olerepr_.propMap.get(attr)
if retEntry is None:
retEntry = self._olerepr_.propMapGet.get(attr)
# Not found so far - See what COM says.
if retEntry is None:
try:
if self.__LazyMap__(attr):
if attr in self._olerepr_.mapFuncs: return self._make_method_(attr)
retEntry = self._olerepr_.propMap.get(attr)
if retEntry is None:
retEntry = self._olerepr_.propMapGet.get(attr)
if retEntry is None:
retEntry = build.MapEntry(self.__AttrToID__(attr), (attr,))
except pythoncom.ole_error:
pass # No prop by that name - retEntry remains None.
if not retEntry is None: # see if in my cache
try:
ret = self._mapCachedItems_[retEntry.dispid]
debug_attr_print ("Cached items has attribute!", ret)
return ret
except (KeyError, AttributeError):
debug_attr_print("Attribute %s not in cache" % attr)
# If we are still here, and have a retEntry, get the OLE item
if not retEntry is None:
invoke_type = _GetDescInvokeType(retEntry, pythoncom.INVOKE_PROPERTYGET)
debug_attr_print("Getting property Id 0x%x from OLE object" % retEntry.dispid)
try:
ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1)
except pythoncom.com_error as details:
if details.hresult in ERRORS_BAD_CONTEXT:
# May be a method.
self._olerepr_.mapFuncs[attr] = retEntry
return self._make_method_(attr)
raise
debug_attr_print("OLE returned ", ret)
return self._get_good_object_(ret)
# no where else to look.
raise AttributeError("%s.%s" % (self._username_, attr))
def __setattr__(self, attr, value):
if attr in self.__dict__: # Fast-track - if already in our dict, just make the assignment.
# XXX - should maybe check method map - if someone assigns to a method,
# it could mean something special (not sure what, tho!)
self.__dict__[attr] = value
return
# Allow property assignment.
debug_attr_print("SetAttr called for %s.%s=%s on DispatchContainer" % (self._username_, attr, repr(value)))
if self._olerepr_:
# Check the "general" property map.
if attr in self._olerepr_.propMap:
entry = self._olerepr_.propMap[attr]
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
return
# Check the specific "put" map.
if attr in self._olerepr_.propMapPut:
entry = self._olerepr_.propMapPut[attr]
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
return
# Try the OLE Object
if self._oleobj_:
if self.__LazyMap__(attr):
# Check the "general" property map.
if attr in self._olerepr_.propMap:
entry = self._olerepr_.propMap[attr]
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
return
# Check the specific "put" map.
if attr in self._olerepr_.propMapPut:
entry = self._olerepr_.propMapPut[attr]
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
return
try:
entry = build.MapEntry(self.__AttrToID__(attr),(attr,))
except pythoncom.com_error:
# No attribute of that name
entry = None
if entry is not None:
try:
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
self._olerepr_.propMap[attr] = entry
debug_attr_print("__setattr__ property %s (id=0x%x) in Dispatch container %s" % (attr, entry.dispid, self._username_))
return
except pythoncom.com_error:
pass
raise AttributeError("Property '%s.%s' can not be set." % (self._username_, attr))

View file

@ -0,0 +1,692 @@
"""Manages the cache of generated Python code.
Description
This file manages the cache of generated Python code. When run from the
command line, it also provides a number of options for managing that cache.
Implementation
Each typelib is generated into a filename of format "{guid}x{lcid}x{major}x{minor}.py"
An external persistant dictionary maps from all known IIDs in all known type libraries
to the type library itself.
Thus, whenever Python code knows the IID of an object, it can find the IID, LCID and version of
the type library which supports it. Given this information, it can find the Python module
with the support.
If necessary, this support can be generated on the fly.
Hacks, to do, etc
Currently just uses a pickled dictionary, but should used some sort of indexed file.
Maybe an OLE2 compound file, or a bsddb file?
"""
import pywintypes, os, sys
import pythoncom
import win32com, win32com.client
import glob
import traceback
from . import CLSIDToClass
import operator
try:
from imp import reload # exported by the imp module in py3k.
except:
pass # a builtin on py2k.
bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also makepy.py
# The global dictionary
clsidToTypelib = {}
# If we have a different version of the typelib generated, this
# maps the "requested version" to the "generated version".
versionRedirectMap = {}
# There is no reason we *must* be readonly in a .zip, but we are now,
# Rather than check for ".zip" or other tricks, PEP302 defines
# a "__loader__" attribute, so we use that.
# (Later, it may become necessary to check if the __loader__ can update files,
# as a .zip loader potentially could - but punt all that until a need arises)
is_readonly = is_zip = hasattr(win32com, "__loader__") and hasattr(win32com.__loader__, "archive")
# A dictionary of ITypeLibrary objects for demand generation explicitly handed to us
# Keyed by usual clsid, lcid, major, minor
demandGeneratedTypeLibraries = {}
import pickle as pickle
def __init__():
# Initialize the module. Called once explicitly at module import below.
try:
_LoadDicts()
except IOError:
Rebuild()
pickleVersion = 1
def _SaveDicts():
if is_readonly:
raise RuntimeError("Trying to write to a readonly gencache ('%s')!" \
% win32com.__gen_path__)
f = open(os.path.join(GetGeneratePath(), "dicts.dat"), "wb")
try:
p = pickle.Pickler(f)
p.dump(pickleVersion)
p.dump(clsidToTypelib)
finally:
f.close()
def _LoadDicts():
# Load the dictionary from a .zip file if that is where we live.
if is_zip:
import io as io
loader = win32com.__loader__
arc_path = loader.archive
dicts_path = os.path.join(win32com.__gen_path__, "dicts.dat")
if dicts_path.startswith(arc_path):
dicts_path = dicts_path[len(arc_path)+1:]
else:
# Hm. See below.
return
try:
data = loader.get_data(dicts_path)
except AttributeError:
# The __loader__ has no get_data method. See below.
return
except IOError:
# Our gencache is in a .zip file (and almost certainly readonly)
# but no dicts file. That actually needn't be fatal for a frozen
# application. Assuming they call "EnsureModule" with the same
# typelib IDs they have been frozen with, that EnsureModule will
# correctly re-build the dicts on the fly. However, objects that
# rely on the gencache but have not done an EnsureModule will
# fail (but their apps are likely to fail running from source
# with a clean gencache anyway, as then they would be getting
# Dynamic objects until the cache is built - so the best answer
# for these apps is to call EnsureModule, rather than freezing
# the dict)
return
f = io.BytesIO(data)
else:
# NOTE: IOError on file open must be caught by caller.
f = open(os.path.join(win32com.__gen_path__, "dicts.dat"), "rb")
try:
p = pickle.Unpickler(f)
version = p.load()
global clsidToTypelib
clsidToTypelib = p.load()
versionRedirectMap.clear()
finally:
f.close()
def GetGeneratedFileName(clsid, lcid, major, minor):
"""Given the clsid, lcid, major and minor for a type lib, return
the file name (no extension) providing this support.
"""
return str(clsid).upper()[1:-1] + "x%sx%sx%s" % (lcid, major, minor)
def SplitGeneratedFileName(fname):
"""Reverse of GetGeneratedFileName()
"""
return tuple(fname.split('x',4))
def GetGeneratePath():
"""Returns the name of the path to generate to.
Checks the directory is OK.
"""
assert not is_readonly, "Why do you want the genpath for a readonly store?"
try:
os.makedirs(win32com.__gen_path__)
#os.mkdir(win32com.__gen_path__)
except os.error:
pass
try:
fname = os.path.join(win32com.__gen_path__, "__init__.py")
os.stat(fname)
except os.error:
f = open(fname,"w")
f.write('# Generated file - this directory may be deleted to reset the COM cache...\n')
f.write('import win32com\n')
f.write('if __path__[:-1] != win32com.__gen_path__: __path__.append(win32com.__gen_path__)\n')
f.close()
return win32com.__gen_path__
#
# The helpers for win32com.client.Dispatch and OCX clients.
#
def GetClassForProgID(progid):
"""Get a Python class for a Program ID
Given a Program ID, return a Python class which wraps the COM object
Returns the Python class, or None if no module is available.
Params
progid -- A COM ProgramID or IID (eg, "Word.Application")
"""
clsid = pywintypes.IID(progid) # This auto-converts named to IDs.
return GetClassForCLSID(clsid)
def GetClassForCLSID(clsid):
"""Get a Python class for a CLSID
Given a CLSID, return a Python class which wraps the COM object
Returns the Python class, or None if no module is available.
Params
clsid -- A COM CLSID (or string repr of one)
"""
# first, take a short-cut - we may already have generated support ready-to-roll.
clsid = str(clsid)
if CLSIDToClass.HasClass(clsid):
return CLSIDToClass.GetClass(clsid)
mod = GetModuleForCLSID(clsid)
if mod is None:
return None
try:
return CLSIDToClass.GetClass(clsid)
except KeyError:
return None
def GetModuleForProgID(progid):
"""Get a Python module for a Program ID
Given a Program ID, return a Python module which contains the
class which wraps the COM object.
Returns the Python module, or None if no module is available.
Params
progid -- A COM ProgramID or IID (eg, "Word.Application")
"""
try:
iid = pywintypes.IID(progid)
except pywintypes.com_error:
return None
return GetModuleForCLSID(iid)
def GetModuleForCLSID(clsid):
"""Get a Python module for a CLSID
Given a CLSID, return a Python module which contains the
class which wraps the COM object.
Returns the Python module, or None if no module is available.
Params
progid -- A COM CLSID (ie, not the description)
"""
clsid_str = str(clsid)
try:
typelibCLSID, lcid, major, minor = clsidToTypelib[clsid_str]
except KeyError:
return None
try:
mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
except ImportError:
mod = None
if mod is not None:
sub_mod = mod.CLSIDToPackageMap.get(clsid_str)
if sub_mod is None:
sub_mod = mod.VTablesToPackageMap.get(clsid_str)
if sub_mod is not None:
sub_mod_name = mod.__name__ + "." + sub_mod
try:
__import__(sub_mod_name)
except ImportError:
info = typelibCLSID, lcid, major, minor
# Force the generation. If this typelibrary has explicitly been added,
# use it (it may not be registered, causing a lookup by clsid to fail)
if info in demandGeneratedTypeLibraries:
info = demandGeneratedTypeLibraries[info]
from . import makepy
makepy.GenerateChildFromTypeLibSpec(sub_mod, info)
# Generate does an import...
mod = sys.modules[sub_mod_name]
return mod
def GetModuleForTypelib(typelibCLSID, lcid, major, minor):
"""Get a Python module for a type library ID
Given the CLSID of a typelibrary, return an imported Python module,
else None
Params
typelibCLSID -- IID of the type library.
major -- Integer major version.
minor -- Integer minor version
lcid -- Integer LCID for the library.
"""
modName = GetGeneratedFileName(typelibCLSID, lcid, major, minor)
mod = _GetModule(modName)
# If the import worked, it doesn't mean we have actually added this
# module to our cache though - check that here.
if "_in_gencache_" not in mod.__dict__:
AddModuleToCache(typelibCLSID, lcid, major, minor)
assert "_in_gencache_" in mod.__dict__
return mod
def MakeModuleForTypelib(typelibCLSID, lcid, major, minor, progressInstance = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
"""Generate support for a type library.
Given the IID, LCID and version information for a type library, generate
and import the necessary support files.
Returns the Python module. No exceptions are caught.
Params
typelibCLSID -- IID of the type library.
major -- Integer major version.
minor -- Integer minor version.
lcid -- Integer LCID for the library.
progressInstance -- Instance to use as progress indicator, or None to
use the GUI progress bar.
"""
from . import makepy
makepy.GenerateFromTypeLibSpec( (typelibCLSID, lcid, major, minor), progressInstance=progressInstance, bForDemand = bForDemand, bBuildHidden = bBuildHidden)
return GetModuleForTypelib(typelibCLSID, lcid, major, minor)
def MakeModuleForTypelibInterface(typelib_ob, progressInstance = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
"""Generate support for a type library.
Given a PyITypeLib interface generate and import the necessary support files. This is useful
for getting makepy support for a typelibrary that is not registered - the caller can locate
and load the type library itself, rather than relying on COM to find it.
Returns the Python module.
Params
typelib_ob -- The type library itself
progressInstance -- Instance to use as progress indicator, or None to
use the GUI progress bar.
"""
from . import makepy
try:
makepy.GenerateFromTypeLibSpec( typelib_ob, progressInstance=progressInstance, bForDemand = bForDemandDefault, bBuildHidden = bBuildHidden)
except pywintypes.com_error:
return None
tla = typelib_ob.GetLibAttr()
guid = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
return GetModuleForTypelib(guid, lcid, major, minor)
def EnsureModuleForTypelibInterface(typelib_ob, progressInstance = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
"""Check we have support for a type library, generating if not.
Given a PyITypeLib interface generate and import the necessary
support files if necessary. This is useful for getting makepy support
for a typelibrary that is not registered - the caller can locate and
load the type library itself, rather than relying on COM to find it.
Returns the Python module.
Params
typelib_ob -- The type library itself
progressInstance -- Instance to use as progress indicator, or None to
use the GUI progress bar.
"""
tla = typelib_ob.GetLibAttr()
guid = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
#If demand generated, save the typelib interface away for later use
if bForDemand:
demandGeneratedTypeLibraries[(str(guid), lcid, major, minor)] = typelib_ob
try:
return GetModuleForTypelib(guid, lcid, major, minor)
except ImportError:
pass
# Generate it.
return MakeModuleForTypelibInterface(typelib_ob, progressInstance, bForDemand, bBuildHidden)
def ForgetAboutTypelibInterface(typelib_ob):
"""Drop any references to a typelib previously added with EnsureModuleForTypelibInterface and forDemand"""
tla = typelib_ob.GetLibAttr()
guid = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
info = str(guid), lcid, major, minor
try:
del demandGeneratedTypeLibraries[info]
except KeyError:
# Not worth raising an exception - maybe they dont know we only remember for demand generated, etc.
print("ForgetAboutTypelibInterface:: Warning - type library with info %s is not being remembered!" % (info,))
# and drop any version redirects to it
for key, val in list(versionRedirectMap.items()):
if val==info:
del versionRedirectMap[key]
def EnsureModule(typelibCLSID, lcid, major, minor, progressInstance = None, bValidateFile=not is_readonly, bForDemand = bForDemandDefault, bBuildHidden = 1):
"""Ensure Python support is loaded for a type library, generating if necessary.
Given the IID, LCID and version information for a type library, check and if
necessary (re)generate, then import the necessary support files. If we regenerate the file, there
is no way to totally snuff out all instances of the old module in Python, and thus we will regenerate the file more than necessary,
unless makepy/genpy is modified accordingly.
Returns the Python module. No exceptions are caught during the generate process.
Params
typelibCLSID -- IID of the type library.
major -- Integer major version.
minor -- Integer minor version
lcid -- Integer LCID for the library.
progressInstance -- Instance to use as progress indicator, or None to
use the GUI progress bar.
bValidateFile -- Whether or not to perform cache validation or not
bForDemand -- Should a complete generation happen now, or on demand?
bBuildHidden -- Should hidden members/attributes etc be generated?
"""
bReloadNeeded = 0
try:
try:
module = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
except ImportError:
# If we get an ImportError
# We may still find a valid cache file under a different MinorVersion #
# (which windows will search out for us)
#print "Loading reg typelib", typelibCLSID, major, minor, lcid
module = None
try:
tlbAttr = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid).GetLibAttr()
# if the above line doesn't throw a pythoncom.com_error, check if
# it is actually a different lib than we requested, and if so, suck it in
if tlbAttr[1] != lcid or tlbAttr[4]!=minor:
#print "Trying 2nd minor #", tlbAttr[1], tlbAttr[3], tlbAttr[4]
try:
module = GetModuleForTypelib(typelibCLSID, tlbAttr[1], tlbAttr[3], tlbAttr[4])
except ImportError:
# We don't have a module, but we do have a better minor
# version - remember that.
minor = tlbAttr[4]
# else module remains None
except pythoncom.com_error:
# couldn't load any typelib - mod remains None
pass
if module is not None and bValidateFile:
assert not is_readonly, "Can't validate in a read-only gencache"
try:
typLibPath = pythoncom.QueryPathOfRegTypeLib(typelibCLSID, major, minor, lcid)
# windows seems to add an extra \0 (via the underlying BSTR)
# The mainwin toolkit does not add this erroneous \0
if typLibPath[-1]=='\0':
typLibPath=typLibPath[:-1]
suf = getattr(os.path, "supports_unicode_filenames", 0)
if not suf:
# can't pass unicode filenames directly - convert
try:
typLibPath=typLibPath.encode(sys.getfilesystemencoding())
except AttributeError: # no sys.getfilesystemencoding
typLibPath=str(typLibPath)
tlbAttributes = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid).GetLibAttr()
except pythoncom.com_error:
# We have a module, but no type lib - we should still
# run with what we have though - the typelib may not be
# deployed here.
bValidateFile = 0
if module is not None and bValidateFile:
assert not is_readonly, "Can't validate in a read-only gencache"
filePathPrefix = "%s\\%s" % (GetGeneratePath(), GetGeneratedFileName(typelibCLSID, lcid, major, minor))
filePath = filePathPrefix + ".py"
filePathPyc = filePathPrefix + ".py"
if __debug__:
filePathPyc = filePathPyc + "c"
else:
filePathPyc = filePathPyc + "o"
# Verify that type library is up to date.
# If we have a differing MinorVersion or genpy has bumped versions, update the file
from . import genpy
if module.MinorVersion != tlbAttributes[4] or genpy.makepy_version != module.makepy_version:
#print "Version skew: %d, %d" % (module.MinorVersion, tlbAttributes[4])
# try to erase the bad file from the cache
try:
os.unlink(filePath)
except os.error:
pass
try:
os.unlink(filePathPyc)
except os.error:
pass
if os.path.isdir(filePathPrefix):
import shutil
shutil.rmtree(filePathPrefix)
minor = tlbAttributes[4]
module = None
bReloadNeeded = 1
else:
minor = module.MinorVersion
filePathPrefix = "%s\\%s" % (GetGeneratePath(), GetGeneratedFileName(typelibCLSID, lcid, major, minor))
filePath = filePathPrefix + ".py"
filePathPyc = filePathPrefix + ".pyc"
#print "Trying py stat: ", filePath
fModTimeSet = 0
try:
pyModTime = os.stat(filePath)[8]
fModTimeSet = 1
except os.error as e:
# If .py file fails, try .pyc file
#print "Trying pyc stat", filePathPyc
try:
pyModTime = os.stat(filePathPyc)[8]
fModTimeSet = 1
except os.error as e:
pass
#print "Trying stat typelib", pyModTime
#print str(typLibPath)
typLibModTime = os.stat(typLibPath)[8]
if fModTimeSet and (typLibModTime > pyModTime):
bReloadNeeded = 1
module = None
except (ImportError, os.error):
module = None
if module is None:
# We need to build an item. If we are in a read-only cache, we
# can't/don't want to do this - so before giving up, check for
# a different minor version in our cache - according to COM, this is OK
if is_readonly:
key = str(typelibCLSID), lcid, major, minor
# If we have been asked before, get last result.
try:
return versionRedirectMap[key]
except KeyError:
pass
# Find other candidates.
items = []
for desc in GetGeneratedInfos():
if key[0]==desc[0] and key[1]==desc[1] and key[2]==desc[2]:
items.append(desc)
if items:
# Items are all identical, except for last tuple element
# We want the latest minor version we have - so just sort and grab last
items.sort()
new_minor = items[-1][3]
ret = GetModuleForTypelib(typelibCLSID, lcid, major, new_minor)
else:
ret = None
# remember and return
versionRedirectMap[key] = ret
return ret
#print "Rebuilding: ", major, minor
module = MakeModuleForTypelib(typelibCLSID, lcid, major, minor, progressInstance, bForDemand = bForDemand, bBuildHidden = bBuildHidden)
# If we replaced something, reload it
if bReloadNeeded:
module = reload(module)
AddModuleToCache(typelibCLSID, lcid, major, minor)
return module
def EnsureDispatch(prog_id, bForDemand = 1): # New fn, so we default the new demand feature to on!
"""Given a COM prog_id, return an object that is using makepy support, building if necessary"""
disp = win32com.client.Dispatch(prog_id)
if not disp.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
try:
ti = disp._oleobj_.GetTypeInfo()
disp_clsid = ti.GetTypeAttr()[0]
tlb, index = ti.GetContainingTypeLib()
tla = tlb.GetLibAttr()
mod = EnsureModule(tla[0], tla[1], tla[3], tla[4], bForDemand=bForDemand)
GetModuleForCLSID(disp_clsid)
# Get the class from the module.
from . import CLSIDToClass
disp_class = CLSIDToClass.GetClass(str(disp_clsid))
disp = disp_class(disp._oleobj_)
except pythoncom.com_error:
raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
return disp
def AddModuleToCache(typelibclsid, lcid, major, minor, verbose = 1, bFlushNow = not is_readonly):
"""Add a newly generated file to the cache dictionary.
"""
fname = GetGeneratedFileName(typelibclsid, lcid, major, minor)
mod = _GetModule(fname)
# if mod._in_gencache_ is already true, then we are reloading this
# module - this doesn't mean anything special though!
mod._in_gencache_ = 1
dict = mod.CLSIDToClassMap
info = str(typelibclsid), lcid, major, minor
for clsid, cls in dict.items():
clsidToTypelib[clsid] = info
dict = mod.CLSIDToPackageMap
for clsid, name in dict.items():
clsidToTypelib[clsid] = info
dict = mod.VTablesToClassMap
for clsid, cls in dict.items():
clsidToTypelib[clsid] = info
dict = mod.VTablesToPackageMap
for clsid, cls in dict.items():
clsidToTypelib[clsid] = info
# If this lib was previously redirected, drop it
if info in versionRedirectMap:
del versionRedirectMap[info]
if bFlushNow:
_SaveDicts()
def GetGeneratedInfos():
zip_pos = win32com.__gen_path__.find(".zip\\")
if zip_pos >= 0:
import zipfile
zip_file = win32com.__gen_path__[:zip_pos+4]
zip_path = win32com.__gen_path__[zip_pos+5:].replace("\\", "/")
zf = zipfile.ZipFile(zip_file)
infos = {}
for n in zf.namelist():
if not n.startswith(zip_path):
continue
base = n[len(zip_path)+1:].split("/")[0]
try:
iid, lcid, major, minor = base.split("x")
lcid = int(lcid)
major = int(major)
minor = int(minor)
iid = pywintypes.IID("{" + iid + "}")
except ValueError:
continue
except pywintypes.com_error:
# invalid IID
continue
infos[(iid, lcid, major, minor)] = 1
zf.close()
return list(infos.keys())
else:
# on the file system
files = glob.glob(win32com.__gen_path__+ "\\*")
ret = []
for file in files:
if not os.path.isdir(file) and not os.path.splitext(file)[1]==".py":
continue
name = os.path.splitext(os.path.split(file)[1])[0]
try:
iid, lcid, major, minor = name.split("x")
iid = pywintypes.IID("{" + iid + "}")
lcid = int(lcid)
major = int(major)
minor = int(minor)
except ValueError:
continue
except pywintypes.com_error:
# invalid IID
continue
ret.append((iid, lcid, major, minor))
return ret
def _GetModule(fname):
"""Given the name of a module in the gen_py directory, import and return it.
"""
mod_name = "win32com.gen_py.%s" % fname
mod = __import__(mod_name)
return sys.modules[mod_name]
def Rebuild(verbose = 1):
"""Rebuild the cache indexes from the file system.
"""
clsidToTypelib.clear()
infos = GetGeneratedInfos()
if verbose and len(infos): # Dont bother reporting this when directory is empty!
print("Rebuilding cache of generated files for COM support...")
for info in infos:
iid, lcid, major, minor = info
if verbose:
print("Checking", GetGeneratedFileName(*info))
try:
AddModuleToCache(iid, lcid, major, minor, verbose, 0)
except:
print("Could not add module %s - %s: %s" % (info, sys.exc_info()[0],sys.exc_info()[1]))
if verbose and len(infos): # Dont bother reporting this when directory is empty!
print("Done.")
_SaveDicts()
def _Dump():
print("Cache is in directory", win32com.__gen_path__)
# Build a unique dir
d = {}
for clsid, (typelibCLSID, lcid, major, minor) in clsidToTypelib.items():
d[typelibCLSID, lcid, major, minor] = None
for typelibCLSID, lcid, major, minor in d.keys():
mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
print("%s - %s" % (mod.__doc__, typelibCLSID))
# Boot up
__init__()
def usage():
usageString = """\
Usage: gencache [-q] [-d] [-r]
-q - Quiet
-d - Dump the cache (typelibrary description and filename).
-r - Rebuild the cache dictionary from the existing .py files
"""
print(usageString)
sys.exit(1)
if __name__=='__main__':
import getopt
try:
opts, args = getopt.getopt(sys.argv[1:], "qrd")
except getopt.error as message:
print(message)
usage()
# we only have options - complain about real args, or none at all!
if len(sys.argv)==1 or args:
print(usage())
verbose = 1
for opt, val in opts:
if opt=='-d': # Dump
_Dump()
if opt=='-r':
Rebuild(verbose)
if opt=='-q':
verbose = 0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,390 @@
# Originally written by Curt Hagenlocher, and various bits
# and pieces by Mark Hammond (and now Greg Stein has had
# a go too :-)
# Note that the main worker code has been moved to genpy.py
# As this is normally run from the command line, it reparses the code each time.
# Now this is nothing more than the command line handler and public interface.
# XXX - TO DO
# XXX - Greg and Mark have some ideas for a revamp - just no
# time - if you want to help, contact us for details.
# Main idea is to drop the classes exported and move to a more
# traditional data driven model.
"""Generate a .py file from an OLE TypeLibrary file.
This module is concerned only with the actual writing of
a .py file. It draws on the @build@ module, which builds
the knowledge of a COM interface.
"""
usageHelp = """ \
Usage:
makepy.py [-i] [-v|q] [-h] [-u] [-o output_file] [-d] [typelib, ...]
-i -- Show information for the specified typelib.
-v -- Verbose output.
-q -- Quiet output.
-h -- Do not generate hidden methods.
-u -- Python 1.5 and earlier: Do NOT convert all Unicode objects to
strings.
Python 1.6 and later: Convert all Unicode objects to strings.
-o -- Create output in a specified output file. If the path leading
to the file does not exist, any missing directories will be
created.
NOTE: -o cannot be used with -d. This will generate an error.
-d -- Generate the base code now and the class code on demand.
Recommended for large type libraries.
typelib -- A TLB, DLL, OCX or anything containing COM type information.
If a typelib is not specified, a window containing a textbox
will open from which you can select a registered type
library.
Examples:
makepy.py -d
Presents a list of registered type libraries from which you can make
a selection.
makepy.py -d "Microsoft Excel 8.0 Object Library"
Generate support for the type library with the specified description
(in this case, the MS Excel object model).
"""
import sys, os, pythoncom
from win32com.client import genpy, selecttlb, gencache
from win32com.client import Dispatch
bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also gencache.py
error = "makepy.error"
def usage():
sys.stderr.write (usageHelp)
sys.exit(2)
def ShowInfo(spec):
if not spec:
tlbSpec = selecttlb.SelectTlb(excludeFlags=selecttlb.FLAG_HIDDEN)
if tlbSpec is None:
return
try:
tlb = pythoncom.LoadRegTypeLib(tlbSpec.clsid, tlbSpec.major, tlbSpec.minor, tlbSpec.lcid)
except pythoncom.com_error: # May be badly registered.
sys.stderr.write("Warning - could not load registered typelib '%s'\n" % (tlbSpec.clsid))
tlb = None
infos = [(tlb, tlbSpec)]
else:
infos = GetTypeLibsForSpec(spec)
for (tlb, tlbSpec) in infos:
desc = tlbSpec.desc
if desc is None:
if tlb is None:
desc = "<Could not load typelib %s>" % (tlbSpec.dll)
else:
desc = tlb.GetDocumentation(-1)[0]
print(desc)
print(" %s, lcid=%s, major=%s, minor=%s" % (tlbSpec.clsid, tlbSpec.lcid, tlbSpec.major, tlbSpec.minor))
print(" >>> # Use these commands in Python code to auto generate .py support")
print(" >>> from win32com.client import gencache")
print(" >>> gencache.EnsureModule('%s', %s, %s, %s)" % (tlbSpec.clsid, tlbSpec.lcid, tlbSpec.major, tlbSpec.minor))
class SimpleProgress(genpy.GeneratorProgress):
"""A simple progress class prints its output to stderr
"""
def __init__(self, verboseLevel):
self.verboseLevel = verboseLevel
def Close(self):
pass
def Finished(self):
if self.verboseLevel>1:
sys.stderr.write("Generation complete..\n")
def SetDescription(self, desc, maxticks = None):
if self.verboseLevel:
sys.stderr.write(desc + "\n")
def Tick(self, desc = None):
pass
def VerboseProgress(self, desc, verboseLevel = 2):
if self.verboseLevel >= verboseLevel:
sys.stderr.write(desc + "\n")
def LogBeginGenerate(self, filename):
self.VerboseProgress("Generating to %s" % filename, 1)
def LogWarning(self, desc):
self.VerboseProgress("WARNING: " + desc, 1)
class GUIProgress(SimpleProgress):
def __init__(self, verboseLevel):
# Import some modules we need to we can trap failure now.
import win32ui, pywin
SimpleProgress.__init__(self, verboseLevel)
self.dialog = None
def Close(self):
if self.dialog is not None:
self.dialog.Close()
self.dialog = None
def Starting(self, tlb_desc):
SimpleProgress.Starting(self, tlb_desc)
if self.dialog is None:
from pywin.dialogs import status
self.dialog=status.ThreadedStatusProgressDialog(tlb_desc)
else:
self.dialog.SetTitle(tlb_desc)
def SetDescription(self, desc, maxticks = None):
self.dialog.SetText(desc)
if maxticks:
self.dialog.SetMaxTicks(maxticks)
def Tick(self, desc = None):
self.dialog.Tick()
if desc is not None:
self.dialog.SetText(desc)
def GetTypeLibsForSpec(arg):
"""Given an argument on the command line (either a file name, library
description, or ProgID of an object) return a list of actual typelibs
to use. """
typelibs = []
try:
try:
tlb = pythoncom.LoadTypeLib(arg)
spec = selecttlb.TypelibSpec(None, 0,0,0)
spec.FromTypelib(tlb, arg)
typelibs.append((tlb, spec))
except pythoncom.com_error:
# See if it is a description
tlbs = selecttlb.FindTlbsWithDescription(arg)
if len(tlbs)==0:
# Maybe it is the name of a COM object?
try:
ob = Dispatch(arg)
# and if so, it must support typelib info
tlb, index = ob._oleobj_.GetTypeInfo().GetContainingTypeLib()
spec = selecttlb.TypelibSpec(None, 0,0,0)
spec.FromTypelib(tlb)
tlbs.append(spec)
except pythoncom.com_error:
pass
if len(tlbs)==0:
print("Could not locate a type library matching '%s'" % (arg))
for spec in tlbs:
# Version numbers not always reliable if enumerated from registry.
# (as some libs use hex, other's dont. Both examples from MS, of course.)
if spec.dll is None:
tlb = pythoncom.LoadRegTypeLib(spec.clsid, spec.major, spec.minor, spec.lcid)
else:
tlb = pythoncom.LoadTypeLib(spec.dll)
# We have a typelib, but it may not be exactly what we specified
# (due to automatic version matching of COM). So we query what we really have!
attr = tlb.GetLibAttr()
spec.major = attr[3]
spec.minor = attr[4]
spec.lcid = attr[1]
typelibs.append((tlb, spec))
return typelibs
except pythoncom.com_error:
t,v,tb=sys.exc_info()
sys.stderr.write ("Unable to load type library from '%s' - %s\n" % (arg, v))
tb = None # Storing tb in a local is a cycle!
sys.exit(1)
def GenerateFromTypeLibSpec(typelibInfo, file = None, verboseLevel = None, progressInstance = None, bUnicodeToString=None, bForDemand = bForDemandDefault, bBuildHidden = 1):
assert bUnicodeToString is None, "this is deprecated and will go away"
if verboseLevel is None:
verboseLevel = 0 # By default, we use no gui and no verbose level!
if bForDemand and file is not None:
raise RuntimeError("You can only perform a demand-build when the output goes to the gen_py directory")
if isinstance(typelibInfo, tuple):
# Tuple
typelibCLSID, lcid, major, minor = typelibInfo
tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
spec = selecttlb.TypelibSpec(typelibCLSID, lcid, major, minor)
spec.FromTypelib(tlb, str(typelibCLSID))
typelibs = [(tlb, spec)]
elif isinstance(typelibInfo, selecttlb.TypelibSpec):
if typelibInfo.dll is None:
# Version numbers not always reliable if enumerated from registry.
tlb = pythoncom.LoadRegTypeLib(typelibInfo.clsid, typelibInfo.major, typelibInfo.minor, typelibInfo.lcid)
else:
tlb = pythoncom.LoadTypeLib(typelibInfo.dll)
typelibs = [(tlb, typelibInfo)]
elif hasattr(typelibInfo, "GetLibAttr"):
# A real typelib object!
# Could also use isinstance(typelibInfo, PyITypeLib) instead, but PyITypeLib is not directly exposed by pythoncom.
# pythoncom.TypeIIDs[pythoncom.IID_ITypeLib] seems to work
tla = typelibInfo.GetLibAttr()
guid = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
spec = selecttlb.TypelibSpec(guid, lcid, major, minor)
typelibs = [(typelibInfo, spec)]
else:
typelibs = GetTypeLibsForSpec(typelibInfo)
if progressInstance is None:
progressInstance = SimpleProgress(verboseLevel)
progress = progressInstance
bToGenDir = (file is None)
for typelib, info in typelibs:
gen = genpy.Generator(typelib, info.dll, progress, bBuildHidden=bBuildHidden)
if file is None:
this_name = gencache.GetGeneratedFileName(info.clsid, info.lcid, info.major, info.minor)
full_name = os.path.join(gencache.GetGeneratePath(), this_name)
if bForDemand:
try: os.unlink(full_name + ".py")
except os.error: pass
try: os.unlink(full_name + ".pyc")
except os.error: pass
try: os.unlink(full_name + ".pyo")
except os.error: pass
if not os.path.isdir(full_name):
os.mkdir(full_name)
outputName = os.path.join(full_name, "__init__.py")
else:
outputName = full_name + ".py"
fileUse = gen.open_writer(outputName)
progress.LogBeginGenerate(outputName)
else:
fileUse = file
worked = False
try:
gen.generate(fileUse, bForDemand)
worked = True
finally:
if file is None:
gen.finish_writer(outputName, fileUse, worked)
if bToGenDir:
progress.SetDescription("Importing module")
gencache.AddModuleToCache(info.clsid, info.lcid, info.major, info.minor)
progress.Close()
def GenerateChildFromTypeLibSpec(child, typelibInfo, verboseLevel = None, progressInstance = None, bUnicodeToString=None):
assert bUnicodeToString is None, "this is deprecated and will go away"
if verboseLevel is None:
verboseLevel = 0 # By default, we use no gui, and no verbose level for the children.
if type(typelibInfo)==type(()):
typelibCLSID, lcid, major, minor = typelibInfo
tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
else:
tlb = typelibInfo
tla = typelibInfo.GetLibAttr()
typelibCLSID = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
spec = selecttlb.TypelibSpec(typelibCLSID, lcid, major, minor)
spec.FromTypelib(tlb, str(typelibCLSID))
typelibs = [(tlb, spec)]
if progressInstance is None:
progressInstance = SimpleProgress(verboseLevel)
progress = progressInstance
for typelib, info in typelibs:
dir_name = gencache.GetGeneratedFileName(info.clsid, info.lcid, info.major, info.minor)
dir_path_name = os.path.join(gencache.GetGeneratePath(), dir_name)
progress.LogBeginGenerate(dir_path_name)
gen = genpy.Generator(typelib, info.dll, progress)
gen.generate_child(child, dir_path_name)
progress.SetDescription("Importing module")
__import__("win32com.gen_py." + dir_name + "." + child)
progress.Close()
def main():
import getopt
hiddenSpec = 1
outputName = None
verboseLevel = 1
doit = 1
bForDemand = bForDemandDefault
try:
opts, args = getopt.getopt(sys.argv[1:], 'vo:huiqd')
for o,v in opts:
if o=='-h':
hiddenSpec = 0
elif o=='-o':
outputName = v
elif o=='-v':
verboseLevel = verboseLevel + 1
elif o=='-q':
verboseLevel = verboseLevel - 1
elif o=='-i':
if len(args)==0:
ShowInfo(None)
else:
for arg in args:
ShowInfo(arg)
doit = 0
elif o=='-d':
bForDemand = not bForDemand
except (getopt.error, error) as msg:
sys.stderr.write (str(msg) + "\n")
usage()
if bForDemand and outputName is not None:
sys.stderr.write("Can not use -d and -o together\n")
usage()
if not doit:
return 0
if len(args)==0:
rc = selecttlb.SelectTlb()
if rc is None:
sys.exit(1)
args = [ rc ]
if outputName is not None:
path = os.path.dirname(outputName)
if path != '' and not os.path.exists(path):
os.makedirs(path)
if sys.version_info > (3,0):
f = open(outputName, "wt", encoding="mbcs")
else:
import codecs # not available in py3k.
f = codecs.open(outputName, "w", "mbcs")
else:
f = None
for arg in args:
GenerateFromTypeLibSpec(arg, f, verboseLevel = verboseLevel, bForDemand = bForDemand, bBuildHidden = hiddenSpec)
if f:
f.close()
if __name__=='__main__':
rc = main()
if rc:
sys.exit(rc)
sys.exit(0)

View file

@ -0,0 +1,160 @@
"""Utilities for selecting and enumerating the Type Libraries installed on the system
"""
import win32api, win32con, pythoncom
class TypelibSpec:
def __init__(self, clsid, lcid, major, minor, flags=0):
self.clsid = str(clsid)
self.lcid = int(lcid)
# We avoid assuming 'major' or 'minor' are integers - when
# read from the registry there is some confusion about if
# they are base 10 or base 16 (they *should* be base 16, but
# how they are written is beyond our control.)
self.major = major
self.minor = minor
self.dll = None
self.desc = None
self.ver_desc = None
self.flags = flags
# For the SelectList
def __getitem__(self, item):
if item==0:
return self.ver_desc
raise IndexError("Cant index me!")
def __lt__(self, other): # rich-cmp/py3k-friendly version
me = (self.ver_desc or "").lower(), (self.desc or "").lower(), self.major, self.minor
them = (other.ver_desc or "").lower(), (other.desc or "").lower(), other.major, other.minor
return me < them
def __eq__(self, other): # rich-cmp/py3k-friendly version
return ((self.ver_desc or "").lower() == (other.ver_desc or "").lower() and
(self.desc or "").lower() == (other.desc or "").lower() and
self.major == other.major and
self.minor == other.minor)
def Resolve(self):
if self.dll is None:
return 0
tlb = pythoncom.LoadTypeLib(self.dll)
self.FromTypelib(tlb, None)
return 1
def FromTypelib(self, typelib, dllName = None):
la = typelib.GetLibAttr()
self.clsid = str(la[0])
self.lcid = la[1]
self.major = la[3]
self.minor = la[4]
if dllName:
self.dll = dllName
def EnumKeys(root):
index = 0
ret = []
while 1:
try:
item = win32api.RegEnumKey(root, index)
except win32api.error:
break
try:
# Note this doesn't handle REG_EXPAND_SZ, but the implementation
# here doesn't need to - that is handled as the data is read.
val = win32api.RegQueryValue(root, item)
except win32api.error:
val = "" # code using this assumes a string.
ret.append((item, val))
index = index + 1
return ret
FLAG_RESTRICTED=1
FLAG_CONTROL=2
FLAG_HIDDEN=4
def EnumTlbs(excludeFlags = 0):
"""Return a list of TypelibSpec objects, one for each registered library.
"""
key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "Typelib")
iids = EnumKeys(key)
results = []
for iid, crap in iids:
try:
key2 = win32api.RegOpenKey(key, str(iid))
except win32api.error:
# A few good reasons for this, including "access denied".
continue
for version, tlbdesc in EnumKeys(key2):
major_minor = version.split('.', 1)
if len(major_minor) < 2:
major_minor.append('0')
# For some reason, this code used to assume the values were hex.
# This seems to not be true - particularly for CDO 1.21
# *sigh* - it appears there are no rules here at all, so when we need
# to know the info, we must load the tlb by filename and request it.
# The Resolve() method on the TypelibSpec does this.
# For this reason, keep the version numbers as strings - that
# way we can't be wrong! Let code that really needs an int to work
# out what to do. FWIW, http://support.microsoft.com/kb/816970 is
# pretty clear that they *should* be hex.
major = major_minor[0]
minor = major_minor[1]
key3 = win32api.RegOpenKey(key2, str(version))
try:
# The "FLAGS" are at this point
flags = int(win32api.RegQueryValue(key3, "FLAGS"))
except (win32api.error, ValueError):
flags = 0
if flags & excludeFlags==0:
for lcid, crap in EnumKeys(key3):
try:
lcid = int(lcid)
except ValueError: # not an LCID entry
continue
# Only care about "{lcid}\win32" key - jump straight there.
try:
key4 = win32api.RegOpenKey(key3, "%s\\win32" % (lcid,))
except win32api.error:
continue
try:
dll, typ = win32api.RegQueryValueEx(key4, None)
if typ==win32con.REG_EXPAND_SZ:
dll = win32api.ExpandEnvironmentStrings(dll)
except win32api.error:
dll = None
spec = TypelibSpec(iid, lcid, major, minor, flags)
spec.dll = dll
spec.desc = tlbdesc
spec.ver_desc = tlbdesc + " (" + version + ")"
results.append(spec)
return results
def FindTlbsWithDescription(desc):
"""Find all installed type libraries with the specified description
"""
ret = []
items = EnumTlbs()
for item in items:
if item.desc==desc:
ret.append(item)
return ret
def SelectTlb(title="Select Library", excludeFlags = 0):
"""Display a list of all the type libraries, and select one. Returns None if cancelled
"""
import pywin.dialogs.list
items = EnumTlbs(excludeFlags)
# fixup versions - we assume hex (see __init__ above)
for i in items:
i.major = int(i.major, 16)
i.minor = int(i.minor, 16)
items.sort()
rc = pywin.dialogs.list.SelectFromLists(title, items, ["Type Library"])
if rc is None:
return None
return items[rc]
# Test code.
if __name__=='__main__':
print(SelectTlb().__dict__)

View file

@ -0,0 +1,243 @@
import win32ui
import win32con
import win32api
import commctrl
import pythoncom
from pywin.mfc import dialog
class TLBrowserException(Exception):
"TypeLib browser internal error"
error = TLBrowserException
FRAMEDLG_STD = win32con.WS_CAPTION | win32con.WS_SYSMENU
SS_STD = win32con.WS_CHILD | win32con.WS_VISIBLE
BS_STD = SS_STD | win32con.WS_TABSTOP
ES_STD = BS_STD | win32con.WS_BORDER
LBS_STD = ES_STD | win32con.LBS_NOTIFY | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL
CBS_STD = ES_STD | win32con.CBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL
typekindmap = {
pythoncom.TKIND_ENUM : 'Enumeration',
pythoncom.TKIND_RECORD : 'Record',
pythoncom.TKIND_MODULE : 'Module',
pythoncom.TKIND_INTERFACE : 'Interface',
pythoncom.TKIND_DISPATCH : 'Dispatch',
pythoncom.TKIND_COCLASS : 'CoClass',
pythoncom.TKIND_ALIAS : 'Alias',
pythoncom.TKIND_UNION : 'Union'
}
TypeBrowseDialog_Parent=dialog.Dialog
class TypeBrowseDialog(TypeBrowseDialog_Parent):
"Browse a type library"
IDC_TYPELIST = 1000
IDC_MEMBERLIST = 1001
IDC_PARAMLIST = 1002
IDC_LISTVIEW = 1003
def __init__(self, typefile = None):
TypeBrowseDialog_Parent.__init__(self, self.GetTemplate())
try:
if typefile:
self.tlb = pythoncom.LoadTypeLib(typefile)
else:
self.tlb = None
except pythoncom.ole_error:
self.MessageBox("The file does not contain type information")
self.tlb = None
self.HookCommand(self.CmdTypeListbox, self.IDC_TYPELIST)
self.HookCommand(self.CmdMemberListbox, self.IDC_MEMBERLIST)
def OnAttachedObjectDeath(self):
self.tlb = None
self.typeinfo = None
self.attr = None
return TypeBrowseDialog_Parent.OnAttachedObjectDeath(self)
def _SetupMenu(self):
menu = win32ui.CreateMenu()
flags=win32con.MF_STRING|win32con.MF_ENABLED
menu.AppendMenu(flags, win32ui.ID_FILE_OPEN, "&Open...")
menu.AppendMenu(flags, win32con.IDCANCEL, "&Close")
mainMenu = win32ui.CreateMenu()
mainMenu.AppendMenu(flags|win32con.MF_POPUP, menu.GetHandle(), "&File")
self.SetMenu(mainMenu)
self.HookCommand(self.OnFileOpen,win32ui.ID_FILE_OPEN)
def OnFileOpen(self, id, code):
openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST
fspec = "Type Libraries (*.tlb, *.olb)|*.tlb;*.olb|OCX Files (*.ocx)|*.ocx|DLL's (*.dll)|*.dll|All Files (*.*)|*.*||"
dlg = win32ui.CreateFileDialog(1, None, None, openFlags, fspec)
if dlg.DoModal() == win32con.IDOK:
try:
self.tlb = pythoncom.LoadTypeLib(dlg.GetPathName())
except pythoncom.ole_error:
self.MessageBox("The file does not contain type information")
self.tlb = None
self._SetupTLB()
def OnInitDialog(self):
self._SetupMenu()
self.typelb = self.GetDlgItem(self.IDC_TYPELIST)
self.memberlb = self.GetDlgItem(self.IDC_MEMBERLIST)
self.paramlb = self.GetDlgItem(self.IDC_PARAMLIST)
self.listview = self.GetDlgItem(self.IDC_LISTVIEW)
# Setup the listview columns
itemDetails = (commctrl.LVCFMT_LEFT, 100, "Item", 0)
self.listview.InsertColumn(0, itemDetails)
itemDetails = (commctrl.LVCFMT_LEFT, 1024, "Details", 0)
self.listview.InsertColumn(1, itemDetails)
if self.tlb is None:
self.OnFileOpen(None,None)
else:
self._SetupTLB()
return TypeBrowseDialog_Parent.OnInitDialog(self)
def _SetupTLB(self):
self.typelb.ResetContent()
self.memberlb.ResetContent()
self.paramlb.ResetContent()
self.typeinfo = None
self.attr = None
if self.tlb is None: return
n = self.tlb.GetTypeInfoCount()
for i in range(n):
self.typelb.AddString(self.tlb.GetDocumentation(i)[0])
def _SetListviewTextItems(self, items):
self.listview.DeleteAllItems()
index = -1
for item in items:
index = self.listview.InsertItem(index+1,item[0])
data = item[1]
if data is None: data = ""
self.listview.SetItemText(index, 1, data)
def SetupAllInfoTypes(self):
infos = self._GetMainInfoTypes() + self._GetMethodInfoTypes()
self._SetListviewTextItems(infos)
def _GetMainInfoTypes(self):
pos = self.typelb.GetCurSel()
if pos<0: return []
docinfo = self.tlb.GetDocumentation(pos)
infos = [('GUID', str(self.attr[0]))]
infos.append(('Help File', docinfo[3]))
infos.append(('Help Context', str(docinfo[2])))
try:
infos.append(('Type Kind', typekindmap[self.tlb.GetTypeInfoType(pos)]))
except:
pass
info = self.tlb.GetTypeInfo(pos)
attr = info.GetTypeAttr()
infos.append(('Attributes', str(attr)))
for j in range(attr[8]):
flags = info.GetImplTypeFlags(j)
refInfo = info.GetRefTypeInfo(info.GetRefTypeOfImplType(j))
doc = refInfo.GetDocumentation(-1)
attr = refInfo.GetTypeAttr()
typeKind = attr[5]
typeFlags = attr[11]
desc = doc[0]
desc = desc + ", Flags=0x%x, typeKind=0x%x, typeFlags=0x%x" % (flags, typeKind, typeFlags)
if flags & pythoncom.IMPLTYPEFLAG_FSOURCE:
desc = desc + "(Source)"
infos.append( ('Implements', desc))
return infos
def _GetMethodInfoTypes(self):
pos = self.memberlb.GetCurSel()
if pos<0: return []
realPos, isMethod = self._GetRealMemberPos(pos)
ret = []
if isMethod:
funcDesc = self.typeinfo.GetFuncDesc(realPos)
id = funcDesc[0]
ret.append(("Func Desc", str(funcDesc)))
else:
id = self.typeinfo.GetVarDesc(realPos)[0]
docinfo = self.typeinfo.GetDocumentation(id)
ret.append(('Help String', docinfo[1]))
ret.append(('Help Context', str(docinfo[2])))
return ret
def CmdTypeListbox(self, id, code):
if code == win32con.LBN_SELCHANGE:
pos = self.typelb.GetCurSel()
if pos >= 0:
self.memberlb.ResetContent()
self.typeinfo = self.tlb.GetTypeInfo(pos)
self.attr = self.typeinfo.GetTypeAttr()
for i in range(self.attr[7]):
id = self.typeinfo.GetVarDesc(i)[0]
self.memberlb.AddString(self.typeinfo.GetNames(id)[0])
for i in range(self.attr[6]):
id = self.typeinfo.GetFuncDesc(i)[0]
self.memberlb.AddString(self.typeinfo.GetNames(id)[0])
self.SetupAllInfoTypes()
return 1
def _GetRealMemberPos(self, pos):
pos = self.memberlb.GetCurSel()
if pos >= self.attr[7]:
return pos - self.attr[7], 1
elif pos >= 0:
return pos, 0
else:
raise error("The position is not valid")
def CmdMemberListbox(self, id, code):
if code == win32con.LBN_SELCHANGE:
self.paramlb.ResetContent()
pos = self.memberlb.GetCurSel()
realPos, isMethod = self._GetRealMemberPos(pos)
if isMethod:
id = self.typeinfo.GetFuncDesc(realPos)[0]
names = self.typeinfo.GetNames(id)
for i in range(len(names)):
if i > 0:
self.paramlb.AddString(names[i])
self.SetupAllInfoTypes()
return 1
def GetTemplate(self):
"Return the template used to create this dialog"
w = 272 # Dialog width
h = 192 # Dialog height
style = FRAMEDLG_STD | win32con.WS_VISIBLE | win32con.DS_SETFONT | win32con.WS_MINIMIZEBOX
template = [['Type Library Browser', (0, 0, w, h), style, None, (8, 'Helv')], ]
template.append([130, "&Type", -1, (10, 10, 62, 9), SS_STD | win32con.SS_LEFT])
template.append([131, None, self.IDC_TYPELIST, (10, 20, 80, 80), LBS_STD])
template.append([130, "&Members", -1, (100, 10, 62, 9), SS_STD | win32con.SS_LEFT])
template.append([131, None, self.IDC_MEMBERLIST, (100, 20, 80, 80), LBS_STD])
template.append([130, "&Parameters", -1, (190, 10, 62, 9), SS_STD | win32con.SS_LEFT])
template.append([131, None, self.IDC_PARAMLIST, (190, 20, 75, 80), LBS_STD])
lvStyle = SS_STD | commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE | commctrl.LVS_ALIGNLEFT | win32con.WS_BORDER | win32con.WS_TABSTOP
template.append(["SysListView32", "", self.IDC_LISTVIEW, (10, 110, 255, 65), lvStyle])
return template
if __name__=='__main__':
import sys
fname = None
try:
fname = sys.argv[1]
except:
pass
dlg = TypeBrowseDialog(fname)
try:
win32api.GetConsoleTitle()
dlg.DoModal()
except:
dlg.CreateWindow(win32ui.GetMainFrame())

View file

@ -0,0 +1,84 @@
"""General client side utilities.
This module contains utility functions, used primarily by advanced COM
programmers, or other COM modules.
"""
import pythoncom
from win32com.client import Dispatch, _get_good_object_
PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
def WrapEnum(ob, resultCLSID = None):
"""Wrap an object in a VARIANT enumerator.
All VT_DISPATCHs returned by the enumerator are converted to wrapper objects
(which may be either a class instance, or a dynamic.Dispatch type object).
"""
if type(ob) != pythoncom.TypeIIDs[pythoncom.IID_IEnumVARIANT]:
ob = ob.QueryInterface(pythoncom.IID_IEnumVARIANT)
return EnumVARIANT(ob, resultCLSID)
class Enumerator:
"""A class that provides indexed access into an Enumerator
By wrapping a PyIEnum* object in this class, you can perform
natural looping and indexing into the Enumerator.
Looping is very efficient, but it should be noted that although random
access is supported, the underlying object is still an enumerator, so
this will force many reset-and-seek operations to find the requested index.
"""
def __init__(self, enum):
self._oleobj_ = enum # a PyIEnumVARIANT
self.index = -1
def __getitem__(self, index):
return self.__GetIndex(index)
def __call__(self, index):
return self.__GetIndex(index)
def __GetIndex(self, index):
if type(index)!=type(0): raise TypeError("Only integer indexes are supported for enumerators")
# NOTE
# In this context, self.index is users purely as a flag to say
# "am I still in sequence". The user may call Next() or Reset() if they
# so choose, in which case self.index will not be correct (although we
# still want to stay in sequence)
if index != self.index + 1:
# Index requested out of sequence.
self._oleobj_.Reset()
if index: self._oleobj_.Skip(index) # if asked for item 1, must skip 1, Python always zero based.
self.index = index
result = self._oleobj_.Next(1)
if len(result):
return self._make_retval_(result[0])
raise IndexError("list index out of range")
def Next(self, count=1):
ret = self._oleobj_.Next(count)
realRets = []
for r in ret:
realRets.append(self._make_retval_(r))
return tuple(realRets) # Convert back to tuple.
def Reset(self):
return self._oleobj_.Reset()
def Clone(self):
return self.__class__( self._oleobj_.Clone(), self.resultCLSID)
def _make_retval_(self, result):
return result
class EnumVARIANT(Enumerator):
def __init__(self, enum, resultCLSID = None):
self.resultCLSID = resultCLSID
Enumerator.__init__(self, enum)
def _make_retval_(self, result):
return _get_good_object_(result, resultCLSID = self.resultCLSID)
class Iterator:
def __init__(self, enum, resultCLSID = None):
self.resultCLSID = resultCLSID
self._iter_ = iter(enum.QueryInterface(pythoncom.IID_IEnumVARIANT))
def __iter__(self):
return self
def __next__(self):
return _get_good_object_(next(self._iter_), resultCLSID = self.resultCLSID)