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 @@
# Empty __init__ file to designate a sub-package.

View file

@ -0,0 +1,65 @@
"""Utilities for Server Side connections.
A collection of helpers for server side connection points.
"""
import pythoncom
from .exception import Exception
import winerror
from win32com import olectl
import win32com.server.util
# Methods implemented by the interfaces.
IConnectionPointContainer_methods = ["EnumConnectionPoints","FindConnectionPoint"]
IConnectionPoint_methods = ["EnumConnections","Unadvise","Advise","GetConnectionPointContainer","GetConnectionInterface"]
class ConnectableServer:
_public_methods_ = IConnectionPointContainer_methods + IConnectionPoint_methods
_com_interfaces_ = [pythoncom.IID_IConnectionPoint, pythoncom.IID_IConnectionPointContainer]
# Clients must set _connect_interfaces_ = [...]
def __init__(self):
self.cookieNo = 0
self.connections = {}
# IConnectionPoint interfaces
def EnumConnections(self):
raise Exception(winerror.E_NOTIMPL)
def GetConnectionInterface(self):
raise Exception(winerror.E_NOTIMPL)
def GetConnectionPointContainer(self):
return win32com.server.util.wrap(self)
def Advise(self, pUnk):
# Creates a connection to the client. Simply allocate a new cookie,
# find the clients interface, and store it in a dictionary.
try:
interface = pUnk.QueryInterface(self._connect_interfaces_[0],pythoncom.IID_IDispatch)
except pythoncom.com_error:
raise Exception(scode=olectl.CONNECT_E_NOCONNECTION)
self.cookieNo = self.cookieNo + 1
self.connections[self.cookieNo] = interface
return self.cookieNo
def Unadvise(self, cookie):
# Destroy a connection - simply delete interface from the map.
try:
del self.connections[cookie]
except KeyError:
raise Exception(scode=winerror.E_UNEXPECTED)
# IConnectionPointContainer interfaces
def EnumConnectionPoints(self):
raise Exception(winerror.E_NOTIMPL)
def FindConnectionPoint(self, iid):
# Find a connection we support. Only support the single event interface.
if iid in self._connect_interfaces_:
return win32com.server.util.wrap(self)
def _BroadcastNotify(self, broadcaster, extraArgs):
# Broadcasts a notification to all connections.
# Ignores clients that fail.
for interface in self.connections.values():
try:
broadcaster(*(interface,)+extraArgs)
except pythoncom.com_error as details:
self._OnNotifyFail(interface, details)
def _OnNotifyFail(self, interface, details):
print("Ignoring COM error to connection - %s" % (repr(details)))

View file

@ -0,0 +1,270 @@
"""Dispatcher
Please see policy.py for a discussion on dispatchers and policies
"""
import pythoncom, traceback, win32api
from sys import exc_info
#
from win32com.server.exception import IsCOMServerException
from win32com.util import IIDToInterfaceName
import win32com
class DispatcherBase:
""" The base class for all Dispatchers.
This dispatcher supports wrapping all operations in exception handlers,
and all the necessary delegation to the policy.
This base class supports the printing of "unexpected" exceptions. Note, however,
that exactly where the output of print goes may not be useful! A derived class may
provide additional semantics for this.
"""
def __init__(self, policyClass, object):
self.policy = policyClass(object)
# The logger we should dump to. If None, we should send to the
# default location (typically 'print')
self.logger = getattr(win32com, "logger", None)
# Note the "return self._HandleException_()" is purely to stop pychecker
# complaining - _HandleException_ will itself raise an exception for the
# pythoncom framework, so the result will never be seen.
def _CreateInstance_(self, clsid, reqIID):
try:
self.policy._CreateInstance_(clsid, reqIID)
return pythoncom.WrapObject(self, reqIID)
except:
return self._HandleException_()
def _QueryInterface_(self, iid):
try:
return self.policy._QueryInterface_(iid)
except:
return self._HandleException_()
def _Invoke_(self, dispid, lcid, wFlags, args):
try:
return self.policy._Invoke_(dispid, lcid, wFlags, args)
except:
return self._HandleException_()
def _GetIDsOfNames_(self, names, lcid):
try:
return self.policy._GetIDsOfNames_(names, lcid)
except:
return self._HandleException_()
def _GetTypeInfo_(self, index, lcid):
try:
return self.policy._GetTypeInfo_(index, lcid)
except:
return self._HandleException_()
def _GetTypeInfoCount_(self):
try:
return self.policy._GetTypeInfoCount_()
except:
return self._HandleException_()
def _GetDispID_(self, name, fdex):
try:
return self.policy._GetDispID_(name, fdex)
except:
return self._HandleException_()
def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
try:
return self.policy._InvokeEx_(dispid, lcid, wFlags, args, kwargs, serviceProvider)
except:
return self._HandleException_()
def _DeleteMemberByName_(self, name, fdex):
try:
return self.policy._DeleteMemberByName_(name, fdex)
except:
return self._HandleException_()
def _DeleteMemberByDispID_(self, id):
try:
return self.policy._DeleteMemberByDispID_(id)
except:
return self._HandleException_()
def _GetMemberProperties_(self, id, fdex):
try:
return self.policy._GetMemberProperties_(id, fdex)
except:
return self._HandleException_()
def _GetMemberName_(self, dispid):
try:
return self.policy._GetMemberName_(dispid)
except:
return self._HandleException_()
def _GetNextDispID_(self, fdex, flags):
try:
return self.policy._GetNextDispID_(fdex, flags)
except:
return self._HandleException_()
def _GetNameSpaceParent_(self):
try:
return self.policy._GetNameSpaceParent_()
except:
return self._HandleException_()
def _HandleException_(self):
"""Called whenever an exception is raised.
Default behaviour is to print the exception.
"""
# If not a COM exception, print it for the developer.
if not IsCOMServerException():
if self.logger is not None:
self.logger.exception("pythoncom server error")
else:
traceback.print_exc()
# But still raise it for the framework.
raise
def _trace_(self, *args):
if self.logger is not None:
record = " ".join(map(str, args))
self.logger.debug(record)
else:
for arg in args[:-1]:
print(arg, end=' ')
print(args[-1])
class DispatcherTrace(DispatcherBase):
"""A dispatcher, which causes a 'print' line for each COM function called.
"""
def _QueryInterface_(self, iid):
rc = DispatcherBase._QueryInterface_(self, iid)
if not rc:
self._trace_("in %s._QueryInterface_ with unsupported IID %s (%s)" % (repr(self.policy._obj_), IIDToInterfaceName(iid),iid))
return rc
def _GetIDsOfNames_(self, names, lcid):
self._trace_("in _GetIDsOfNames_ with '%s' and '%d'\n" % (names, lcid))
return DispatcherBase._GetIDsOfNames_(self, names, lcid)
def _GetTypeInfo_(self, index, lcid):
self._trace_("in _GetTypeInfo_ with index=%d, lcid=%d\n" % (index, lcid))
return DispatcherBase._GetTypeInfo_(self, index, lcid)
def _GetTypeInfoCount_(self):
self._trace_("in _GetTypeInfoCount_\n")
return DispatcherBase._GetTypeInfoCount_(self)
def _Invoke_(self, dispid, lcid, wFlags, args):
self._trace_("in _Invoke_ with", dispid, lcid, wFlags, args)
return DispatcherBase._Invoke_(self, dispid, lcid, wFlags, args)
def _GetDispID_(self, name, fdex):
self._trace_("in _GetDispID_ with", name, fdex)
return DispatcherBase._GetDispID_(self, name, fdex)
def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
self._trace_("in %r._InvokeEx_-%s%r [%x,%s,%r]" % (self.policy._obj_, dispid, args, wFlags, lcid, serviceProvider))
return DispatcherBase._InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider)
def _DeleteMemberByName_(self, name, fdex):
self._trace_("in _DeleteMemberByName_ with", name, fdex)
return DispatcherBase._DeleteMemberByName_(self, name, fdex)
def _DeleteMemberByDispID_(self, id):
self._trace_("in _DeleteMemberByDispID_ with", id)
return DispatcherBase._DeleteMemberByDispID_(self, id)
def _GetMemberProperties_(self, id, fdex):
self._trace_("in _GetMemberProperties_ with", id, fdex)
return DispatcherBase._GetMemberProperties_(self, id, fdex)
def _GetMemberName_(self, dispid):
self._trace_("in _GetMemberName_ with", dispid)
return DispatcherBase._GetMemberName_(self, dispid)
def _GetNextDispID_(self, fdex, flags):
self._trace_("in _GetNextDispID_ with", fdex, flags)
return DispatcherBase._GetNextDispID_(self, fdex, flags)
def _GetNameSpaceParent_(self):
self._trace_("in _GetNameSpaceParent_")
return DispatcherBase._GetNameSpaceParent_(self)
class DispatcherWin32trace(DispatcherTrace):
"""A tracing dispatcher that sends its output to the win32trace remote collector.
"""
def __init__(self, policyClass, object):
DispatcherTrace.__init__(self, policyClass, object)
if self.logger is None:
# If we have no logger, setup our output.
import win32traceutil # Sets up everything.
self._trace_("Object with win32trace dispatcher created (object=%s)" % repr(object))
class DispatcherOutputDebugString(DispatcherTrace):
"""A tracing dispatcher that sends its output to win32api.OutputDebugString
"""
def _trace_(self, *args):
for arg in args[:-1]:
win32api.OutputDebugString(str(arg)+" ")
win32api.OutputDebugString(str(args[-1])+"\n")
class DispatcherWin32dbg(DispatcherBase):
"""A source-level debugger dispatcher
A dispatcher which invokes the debugger as an object is instantiated, or
when an unexpected exception occurs.
Requires Pythonwin.
"""
def __init__(self, policyClass, ob):
# No one uses this, and it just causes py2exe to drag all of
# pythonwin in.
#import pywin.debugger
pywin.debugger.brk()
print("The DispatcherWin32dbg dispatcher is deprecated!")
print("Please let me know if this is a problem.")
print("Uncomment the relevant lines in dispatcher.py to re-enable")
# DEBUGGER Note - You can either:
# * Hit Run and wait for a (non Exception class) exception to occur!
# * Set a breakpoint and hit run.
# * Step into the object creation (a few steps away!)
DispatcherBase.__init__(self, policyClass, ob)
def _HandleException_(self):
""" Invoke the debugger post mortem capability """
# Save details away.
typ, val, tb = exc_info()
#import pywin.debugger, pywin.debugger.dbgcon
debug = 0
try:
raise typ(val)
except Exception: # AARG - What is this Exception???
# Use some inside knowledge to borrow a Debugger option which dictates if we
# stop at "expected" exceptions.
debug = pywin.debugger.GetDebugger().get_option(pywin.debugger.dbgcon.OPT_STOP_EXCEPTIONS)
except:
debug = 1
if debug:
try:
pywin.debugger.post_mortem(tb, typ, val) # The original exception
except:
traceback.print_exc()
# But still raise it.
del tb
raise
try:
import win32trace
DefaultDebugDispatcher = DispatcherWin32trace
except ImportError: # no win32trace module - just use a print based one.
DefaultDebugDispatcher = DispatcherTrace

View file

@ -0,0 +1,91 @@
"""Exception Handling
Exceptions
To better support COM exceptions, the framework allows for an instance to be
raised. This instance may have a certain number of known attributes, which are
translated into COM exception details.
This means, for example, that Python could raise a COM exception that includes details
on a Help file and location, and a description for the user.
This module provides a class which provides the necessary attributes.
"""
import sys, pythoncom
# Note that we derive from com_error, which derives from exceptions.Exception
# Also note that we dont support "self.args", as we dont support tuple-unpacking
class COMException(pythoncom.com_error):
"""An Exception object that is understood by the framework.
If the framework is presented with an exception of type class,
it looks for certain known attributes on this class to provide rich
error information to the caller.
It should be noted that the framework supports providing this error
information via COM Exceptions, or via the ISupportErrorInfo interface.
By using this class, you automatically provide rich error information to the
server.
"""
def __init__(self, description = None, scode = None,
source = None, helpfile = None, helpContext = None,
desc = None, hresult = None):
"""Initialize an exception
**Params**
description -- A string description for the exception.
scode -- An integer scode to be returned to the server, if necessary.
The pythoncom framework defaults this to be DISP_E_EXCEPTION if not specified otherwise.
source -- A string which identifies the source of the error.
helpfile -- A string which points to a help file which contains details on the error.
helpContext -- An integer context in the help file.
desc -- A short-cut for description.
hresult -- A short-cut for scode.
"""
# convert a WIN32 error into an HRESULT
scode = scode or hresult
if scode and scode != 1: # We dont want S_FALSE mapped!
if scode >= -32768 and scode < 32768:
# this is HRESULT_FROM_WIN32()
scode = -2147024896 | (scode & 0x0000FFFF)
self.scode = scode
self.description = description or desc
if scode==1 and not self.description:
self.description = "S_FALSE"
elif scode and not self.description:
self.description = pythoncom.GetScodeString(scode)
self.source = source
self.helpfile = helpfile
self.helpcontext = helpContext
# todo - fill in the exception value
pythoncom.com_error.__init__(self, scode, self.description, None, -1)
def __repr__(self):
return "<COM Exception - scode=%s, desc=%s>" % (self.scode, self.description)
# Old name for the COMException class.
# Do NOT use the name Exception, as it is now a built-in
# COMException is the new, official name.
Exception = COMException
def IsCOMException(t = None):
if t is None:
t = sys.exc_info()[0]
try:
return issubclass(t, pythoncom.com_error)
except TypeError: # 1.5 in -X mode?
return t is pythoncon.com_error
def IsCOMServerException(t = None):
if t is None:
t = sys.exc_info()[0]
try:
return issubclass(t, COMException)
except TypeError: # String exception
return 0

View file

@ -0,0 +1,22 @@
# Class factory utilities.
import pythoncom
def RegisterClassFactories(clsids, flags = None, clsctx = None):
"""Given a list of CLSID, create and register class factories.
Returns a list, which should be passed to RevokeClassFactories
"""
if flags is None: flags = pythoncom.REGCLS_MULTIPLEUSE|pythoncom.REGCLS_SUSPENDED
if clsctx is None: clsctx = pythoncom.CLSCTX_LOCAL_SERVER
ret = []
for clsid in clsids:
# Some server append '-Embedding' etc
if clsid[0] not in ['-', '/']:
factory = pythoncom.MakePyFactory(clsid)
regId = pythoncom.CoRegisterClassObject(clsid, factory, clsctx, flags)
ret.append((factory, regId))
return ret
def RevokeClassFactories(infos):
for factory, revokeId in infos:
pythoncom.CoRevokeClassObject(revokeId)

View file

@ -0,0 +1,49 @@
# LocalServer .EXE support for Python.
#
# This is designed to be used as a _script_ file by pythonw.exe
#
# In some cases, you could also use Python.exe, which will create
# a console window useful for debugging.
#
# NOTE: When NOT running in any sort of debugging mode,
# 'print' statements may fail, as sys.stdout is not valid!!!
#
# Usage:
# wpython.exe LocalServer.py clsid [, clsid]
import sys
sys.coinit_flags = 2
import pythoncom
import win32api
from win32com.server import factory
usage = """\
Invalid command line arguments
This program provides LocalServer COM support
for Python COM objects.
It is typically run automatically by COM, passing as arguments
The ProgID or CLSID of the Python Server(s) to be hosted
"""
def serve(clsids):
infos = factory.RegisterClassFactories(clsids)
pythoncom.EnableQuitMessage(win32api.GetCurrentThreadId())
pythoncom.CoResumeClassObjects()
pythoncom.PumpMessages()
factory.RevokeClassFactories( infos )
pythoncom.CoUninitialize()
def main():
if len(sys.argv)==1:
win32api.MessageBox(0, usage, "Python COM Server")
sys.exit(1)
serve(sys.argv[1:])
if __name__=='__main__':
main()

View file

@ -0,0 +1,750 @@
"""Policies
Note that Dispatchers are now implemented in "dispatcher.py", but
are still documented here.
Policies
A policy is an object which manages the interaction between a public
Python object, and COM . In simple terms, the policy object is the
object which is actually called by COM, and it invokes the requested
method, fetches/sets the requested property, etc. See the
@win32com.server.policy.CreateInstance@ method for a description of
how a policy is specified or created.
Exactly how a policy determines which underlying object method/property
is obtained is up to the policy. A few policies are provided, but you
can build your own. See each policy class for a description of how it
implements its policy.
There is a policy that allows the object to specify exactly which
methods and properties will be exposed. There is also a policy that
will dynamically expose all Python methods and properties - even those
added after the object has been instantiated.
Dispatchers
A Dispatcher is a level in front of a Policy. A dispatcher is the
thing which actually receives the COM calls, and passes them to the
policy object (which in turn somehow does something with the wrapped
object).
It is important to note that a policy does not need to have a dispatcher.
A dispatcher has the same interface as a policy, and simply steps in its
place, delegating to the real policy. The primary use for a Dispatcher
is to support debugging when necessary, but without imposing overheads
when not (ie, by not using a dispatcher at all).
There are a few dispatchers provided - "tracing" dispatchers which simply
prints calls and args (including a variation which uses
win32api.OutputDebugString), and a "debugger" dispatcher, which can
invoke the debugger when necessary.
Error Handling
It is important to realise that the caller of these interfaces may
not be Python. Therefore, general Python exceptions and tracebacks aren't
much use.
In general, there is an Exception class that should be raised, to allow
the framework to extract rich COM type error information.
The general rule is that the **only** exception returned from Python COM
Server code should be an Exception instance. Any other Python exception
should be considered an implementation bug in the server (if not, it
should be handled, and an appropriate Exception instance raised). Any
other exception is considered "unexpected", and a dispatcher may take
special action (see Dispatchers above)
Occasionally, the implementation will raise the policy.error error.
This usually means there is a problem in the implementation that the
Python programmer should fix.
For example, if policy is asked to wrap an object which it can not
support (because, eg, it does not provide _public_methods_ or _dynamic_)
then policy.error will be raised, indicating it is a Python programmers
problem, rather than a COM error.
"""
__author__ = "Greg Stein and Mark Hammond"
import win32api
import winerror
import sys
import types
import pywintypes
import win32con, pythoncom
#Import a few important constants to speed lookups.
from pythoncom import \
DISPATCH_METHOD, DISPATCH_PROPERTYGET, DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF, \
DISPID_UNKNOWN, DISPID_VALUE, DISPID_PROPERTYPUT, DISPID_NEWENUM, \
DISPID_EVALUATE, DISPID_CONSTRUCTOR, DISPID_DESTRUCTOR, DISPID_COLLECT,DISPID_STARTENUM
S_OK = 0
# Few more globals to speed things.
IDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
IUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]
from .exception import COMException
error = __name__ + " error"
regSpec = 'CLSID\\%s\\PythonCOM'
regPolicy = 'CLSID\\%s\\PythonCOMPolicy'
regDispatcher = 'CLSID\\%s\\PythonCOMDispatcher'
regAddnPath = 'CLSID\\%s\\PythonCOMPath'
def CreateInstance(clsid, reqIID):
"""Create a new instance of the specified IID
The COM framework **always** calls this function to create a new
instance for the specified CLSID. This function looks up the
registry for the name of a policy, creates the policy, and asks the
policy to create the specified object by calling the _CreateInstance_ method.
Exactly how the policy creates the instance is up to the policy. See the
specific policy documentation for more details.
"""
# First see is sys.path should have something on it.
try:
addnPaths = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
regAddnPath % clsid).split(';')
for newPath in addnPaths:
if newPath not in sys.path:
sys.path.insert(0, newPath)
except win32api.error:
pass
try:
policy = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
regPolicy % clsid)
policy = resolve_func(policy)
except win32api.error:
policy = DefaultPolicy
try:
dispatcher = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
regDispatcher % clsid)
if dispatcher: dispatcher = resolve_func(dispatcher)
except win32api.error:
dispatcher = None
if dispatcher:
retObj = dispatcher(policy, None)
else:
retObj = policy(None)
return retObj._CreateInstance_(clsid, reqIID)
class BasicWrapPolicy:
"""The base class of policies.
Normally not used directly (use a child class, instead)
This policy assumes we are wrapping another object
as the COM server. This supports the delegation of the core COM entry points
to either the wrapped object, or to a child class.
This policy supports the following special attributes on the wrapped object
_query_interface_ -- A handler which can respond to the COM 'QueryInterface' call.
_com_interfaces_ -- An optional list of IIDs which the interface will assume are
valid for the object.
_invoke_ -- A handler which can respond to the COM 'Invoke' call. If this attribute
is not provided, then the default policy implementation is used. If this attribute
does exist, it is responsible for providing all required functionality - ie, the
policy _invoke_ method is not invoked at all (and nor are you able to call it!)
_getidsofnames_ -- A handler which can respond to the COM 'GetIDsOfNames' call. If this attribute
is not provided, then the default policy implementation is used. If this attribute
does exist, it is responsible for providing all required functionality - ie, the
policy _getidsofnames_ method is not invoked at all (and nor are you able to call it!)
IDispatchEx functionality:
_invokeex_ -- Very similar to _invoke_, except slightly different arguments are used.
And the result is just the _real_ result (rather than the (hresult, argErr, realResult)
tuple that _invoke_ uses.
This is the new, prefered handler (the default _invoke_ handler simply called _invokeex_)
_getdispid_ -- Very similar to _getidsofnames_, except slightly different arguments are used,
and only 1 property at a time can be fetched (which is all we support in getidsofnames anyway!)
This is the new, prefered handler (the default _invoke_ handler simply called _invokeex_)
_getnextdispid_- uses self._name_to_dispid_ to enumerate the DISPIDs
"""
def __init__(self, object):
"""Initialise the policy object
Params:
object -- The object to wrap. May be None *iff* @BasicWrapPolicy._CreateInstance_@ will be
called immediately after this to setup a brand new object
"""
if object is not None:
self._wrap_(object)
def _CreateInstance_(self, clsid, reqIID):
"""Creates a new instance of a **wrapped** object
This method looks up a "@win32com.server.policy.regSpec@" % clsid entry
in the registry (using @DefaultPolicy@)
"""
try:
classSpec = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
regSpec % clsid)
except win32api.error:
raise error("The object is not correctly registered - %s key can not be read" % (regSpec % clsid))
myob = call_func(classSpec)
self._wrap_(myob)
try:
return pythoncom.WrapObject(self, reqIID)
except pythoncom.com_error as xxx_todo_changeme:
(hr, desc, exc, arg) = xxx_todo_changeme.args
from win32com.util import IIDToInterfaceName
desc = "The object '%r' was created, but does not support the " \
"interface '%s'(%s): %s" \
% (myob, IIDToInterfaceName(reqIID), reqIID, desc)
raise pythoncom.com_error(hr, desc, exc, arg)
def _wrap_(self, object):
"""Wraps up the specified object.
This function keeps a reference to the passed
object, and may interogate it to determine how to respond to COM requests, etc.
"""
# We "clobber" certain of our own methods with ones
# provided by the wrapped object, iff they exist.
self._name_to_dispid_ = { }
ob = self._obj_ = object
if hasattr(ob, '_query_interface_'):
self._query_interface_ = ob._query_interface_
if hasattr(ob, '_invoke_'):
self._invoke_ = ob._invoke_
if hasattr(ob, '_invokeex_'):
self._invokeex_ = ob._invokeex_
if hasattr(ob, '_getidsofnames_'):
self._getidsofnames_ = ob._getidsofnames_
if hasattr(ob, '_getdispid_'):
self._getdispid_ = ob._getdispid_
# Allow for override of certain special attributes.
if hasattr(ob, '_com_interfaces_'):
self._com_interfaces_ = []
# Allow interfaces to be specified by name.
for i in ob._com_interfaces_:
if type(i) != pywintypes.IIDType:
# Prolly a string!
if i[0] != "{":
i = pythoncom.InterfaceNames[i]
else:
i = pythoncom.MakeIID(i)
self._com_interfaces_.append(i)
else:
self._com_interfaces_ = [ ]
# "QueryInterface" handling.
def _QueryInterface_(self, iid):
"""The main COM entry-point for QueryInterface.
This checks the _com_interfaces_ attribute and if the interface is not specified
there, it calls the derived helper _query_interface_
"""
if iid in self._com_interfaces_:
return 1
return self._query_interface_(iid)
def _query_interface_(self, iid):
"""Called if the object does not provide the requested interface in _com_interfaces_,
and does not provide a _query_interface_ handler.
Returns a result to the COM framework indicating the interface is not supported.
"""
return 0
# "Invoke" handling.
def _Invoke_(self, dispid, lcid, wFlags, args):
"""The main COM entry-point for Invoke.
This calls the _invoke_ helper.
"""
#Translate a possible string dispid to real dispid.
if type(dispid) == type(""):
try:
dispid = self._name_to_dispid_[dispid.lower()]
except KeyError:
raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
return self._invoke_(dispid, lcid, wFlags, args)
def _invoke_(self, dispid, lcid, wFlags, args):
# Delegates to the _invokeex_ implementation. This allows
# a custom policy to define _invokeex_, and automatically get _invoke_ too.
return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)
# "GetIDsOfNames" handling.
def _GetIDsOfNames_(self, names, lcid):
"""The main COM entry-point for GetIDsOfNames.
This checks the validity of the arguments, and calls the _getidsofnames_ helper.
"""
if len(names) > 1:
raise COMException(scode = winerror.DISP_E_INVALID, desc="Cannot support member argument names")
return self._getidsofnames_(names, lcid)
def _getidsofnames_(self, names, lcid):
### note: lcid is being ignored...
return (self._getdispid_(names[0], 0), )
# IDispatchEx support for policies. Most of the IDispathEx functionality
# by default will raise E_NOTIMPL. Thus it is not necessary for derived
# policies to explicitely implement all this functionality just to not implement it!
def _GetDispID_(self, name, fdex):
return self._getdispid_(name, fdex)
def _getdispid_(self, name, fdex):
try:
### TODO - look at the fdex flags!!!
return self._name_to_dispid_[name.lower()]
except KeyError:
raise COMException(scode = winerror.DISP_E_UNKNOWNNAME)
# "InvokeEx" handling.
def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
"""The main COM entry-point for InvokeEx.
This calls the _invokeex_ helper.
"""
#Translate a possible string dispid to real dispid.
if type(dispid) == type(""):
try:
dispid = self._name_to_dispid_[dispid.lower()]
except KeyError:
raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
return self._invokeex_(dispid, lcid, wFlags, args, kwargs, serviceProvider)
def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
"""A stub for _invokeex_ - should never be called.
Simply raises an exception.
"""
# Base classes should override this method (and not call the base)
raise error("This class does not provide _invokeex_ semantics")
def _DeleteMemberByName_(self, name, fdex):
return self._deletememberbyname_(name, fdex)
def _deletememberbyname_(self, name, fdex):
raise COMException(scode = winerror.E_NOTIMPL)
def _DeleteMemberByDispID_(self, id):
return self._deletememberbydispid(id)
def _deletememberbydispid_(self, id):
raise COMException(scode = winerror.E_NOTIMPL)
def _GetMemberProperties_(self, id, fdex):
return self._getmemberproperties_(id, fdex)
def _getmemberproperties_(self, id, fdex):
raise COMException(scode = winerror.E_NOTIMPL)
def _GetMemberName_(self, dispid):
return self._getmembername_(dispid)
def _getmembername_(self, dispid):
raise COMException(scode = winerror.E_NOTIMPL)
def _GetNextDispID_(self, fdex, dispid):
return self._getnextdispid_(fdex, dispid)
def _getnextdispid_(self, fdex, dispid):
ids = list(self._name_to_dispid_.values())
ids.sort()
if DISPID_STARTENUM in ids: ids.remove(DISPID_STARTENUM)
if dispid==DISPID_STARTENUM:
return ids[0]
else:
try:
return ids[ids.index(dispid)+1]
except ValueError: # dispid not in list?
raise COMException(scode = winerror.E_UNEXPECTED)
except IndexError: # No more items
raise COMException(scode = winerror.S_FALSE)
def _GetNameSpaceParent_(self):
return self._getnamespaceparent()
def _getnamespaceparent_(self):
raise COMException(scode = winerror.E_NOTIMPL)
class MappedWrapPolicy(BasicWrapPolicy):
"""Wraps an object using maps to do its magic
This policy wraps up a Python object, using a number of maps
which translate from a Dispatch ID and flags, into an object to call/getattr, etc.
It is the responsibility of derived classes to determine exactly how the
maps are filled (ie, the derived classes determine the map filling policy.
This policy supports the following special attributes on the wrapped object
_dispid_to_func_/_dispid_to_get_/_dispid_to_put_ -- These are dictionaries
(keyed by integer dispid, values are string attribute names) which the COM
implementation uses when it is processing COM requests. Note that the implementation
uses this dictionary for its own purposes - not a copy - which means the contents of
these dictionaries will change as the object is used.
"""
def _wrap_(self, object):
BasicWrapPolicy._wrap_(self, object)
ob = self._obj_
if hasattr(ob, '_dispid_to_func_'):
self._dispid_to_func_ = ob._dispid_to_func_
else:
self._dispid_to_func_ = { }
if hasattr(ob, '_dispid_to_get_'):
self._dispid_to_get_ = ob._dispid_to_get_
else:
self._dispid_to_get_ = { }
if hasattr(ob, '_dispid_to_put_'):
self._dispid_to_put_ = ob._dispid_to_put_
else:
self._dispid_to_put_ = { }
def _getmembername_(self, dispid):
if dispid in self._dispid_to_func_:
return self._dispid_to_func_[dispid]
elif dispid in self._dispid_to_get_:
return self._dispid_to_get_[dispid]
elif dispid in self._dispid_to_put_:
return self._dispid_to_put_[dispid]
else:
raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND)
class DesignatedWrapPolicy(MappedWrapPolicy):
"""A policy which uses a mapping to link functions and dispid
A MappedWrappedPolicy which allows the wrapped object to specify, via certain
special named attributes, exactly which methods and properties are exposed.
All a wrapped object need do is provide the special attributes, and the policy
will handle everything else.
Attributes:
_public_methods_ -- Required, unless a typelib GUID is given -- A list
of strings, which must be the names of methods the object
provides. These methods will be exposed and callable
from other COM hosts.
_public_attrs_ A list of strings, which must be the names of attributes on the object.
These attributes will be exposed and readable and possibly writeable from other COM hosts.
_readonly_attrs_ -- A list of strings, which must also appear in _public_attrs. These
attributes will be readable, but not writable, by other COM hosts.
_value_ -- A method that will be called if the COM host requests the "default" method
(ie, calls Invoke with dispid==DISPID_VALUE)
_NewEnum -- A method that will be called if the COM host requests an enumerator on the
object (ie, calls Invoke with dispid==DISPID_NEWENUM.)
It is the responsibility of the method to ensure the returned
object conforms to the required Enum interface.
_typelib_guid_ -- The GUID of the typelibrary with interface definitions we use.
_typelib_version_ -- A tuple of (major, minor) with a default of 1,1
_typelib_lcid_ -- The LCID of the typelib, default = LOCALE_USER_DEFAULT
_Evaluate -- Dunno what this means, except the host has called Invoke with dispid==DISPID_EVALUATE!
See the COM documentation for details.
"""
def _wrap_(self, ob):
# If we have nominated universal interfaces to support, load them now
tlb_guid = getattr(ob, '_typelib_guid_', None)
if tlb_guid is not None:
tlb_major, tlb_minor = getattr(ob, '_typelib_version_', (1,0))
tlb_lcid = getattr(ob, '_typelib_lcid_', 0)
from win32com import universal
# XXX - what if the user wants to implement interfaces from multiple
# typelibs?
# Filter out all 'normal' IIDs (ie, IID objects and strings starting with {
interfaces = [i for i in getattr(ob, '_com_interfaces_', [])
if type(i) != pywintypes.IIDType and not i.startswith("{")]
universal_data = universal.RegisterInterfaces(tlb_guid, tlb_lcid,
tlb_major, tlb_minor, interfaces)
else:
universal_data = []
MappedWrapPolicy._wrap_(self, ob)
if not hasattr(ob, '_public_methods_') and not hasattr(ob, "_typelib_guid_"):
raise error("Object does not support DesignatedWrapPolicy, as it does not have either _public_methods_ or _typelib_guid_ attributes.")
# Copy existing _dispid_to_func_ entries to _name_to_dispid_
for dispid, name in self._dispid_to_func_.items():
self._name_to_dispid_[name.lower()]=dispid
for dispid, name in self._dispid_to_get_.items():
self._name_to_dispid_[name.lower()]=dispid
for dispid, name in self._dispid_to_put_.items():
self._name_to_dispid_[name.lower()]=dispid
# Patch up the universal stuff.
for dispid, invkind, name in universal_data:
self._name_to_dispid_[name.lower()]=dispid
if invkind == DISPATCH_METHOD:
self._dispid_to_func_[dispid] = name
elif invkind in (DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF):
self._dispid_to_put_[dispid] = name
elif invkind == DISPATCH_PROPERTYGET:
self._dispid_to_get_[dispid] = name
else:
raise ValueError("unexpected invkind: %d (%s)" % (invkind,name))
# look for reserved methods
if hasattr(ob, '_value_'):
self._dispid_to_get_[DISPID_VALUE] = '_value_'
self._dispid_to_put_[DISPID_PROPERTYPUT] = '_value_'
if hasattr(ob, '_NewEnum'):
self._name_to_dispid_['_newenum'] = DISPID_NEWENUM
self._dispid_to_func_[DISPID_NEWENUM] = '_NewEnum'
if hasattr(ob, '_Evaluate'):
self._name_to_dispid_['_evaluate'] = DISPID_EVALUATE
self._dispid_to_func_[DISPID_EVALUATE] = '_Evaluate'
next_dispid = self._allocnextdispid(999)
# note: funcs have precedence over attrs (install attrs first)
if hasattr(ob, '_public_attrs_'):
if hasattr(ob, '_readonly_attrs_'):
readonly = ob._readonly_attrs_
else:
readonly = [ ]
for name in ob._public_attrs_:
dispid = self._name_to_dispid_.get(name.lower())
if dispid is None:
dispid = next_dispid
self._name_to_dispid_[name.lower()] = dispid
next_dispid = self._allocnextdispid(next_dispid)
self._dispid_to_get_[dispid] = name
if name not in readonly:
self._dispid_to_put_[dispid] = name
for name in getattr(ob, "_public_methods_", []):
dispid = self._name_to_dispid_.get(name.lower())
if dispid is None:
dispid = next_dispid
self._name_to_dispid_[name.lower()] = dispid
next_dispid = self._allocnextdispid(next_dispid)
self._dispid_to_func_[dispid] = name
self._typeinfos_ = None # load these on demand.
def _build_typeinfos_(self):
# Can only ever be one for now.
tlb_guid = getattr(self._obj_, '_typelib_guid_', None)
if tlb_guid is None:
return []
tlb_major, tlb_minor = getattr(self._obj_, '_typelib_version_', (1,0))
tlb = pythoncom.LoadRegTypeLib(tlb_guid, tlb_major, tlb_minor)
typecomp = tlb.GetTypeComp()
# Not 100% sure what semantics we should use for the default interface.
# Look for the first name in _com_interfaces_ that exists in the typelib.
for iname in self._obj_._com_interfaces_:
try:
type_info, type_comp = typecomp.BindType(iname)
if type_info is not None:
return [type_info]
except pythoncom.com_error:
pass
return []
def _GetTypeInfoCount_(self):
if self._typeinfos_ is None:
self._typeinfos_ = self._build_typeinfos_()
return len(self._typeinfos_)
def _GetTypeInfo_(self, index, lcid):
if self._typeinfos_ is None:
self._typeinfos_ = self._build_typeinfos_()
if index < 0 or index >= len(self._typeinfos_):
raise COMException(scode=winerror.DISP_E_BADINDEX)
return 0, self._typeinfos_[index]
def _allocnextdispid(self, last_dispid):
while 1:
last_dispid = last_dispid + 1
if last_dispid not in self._dispid_to_func_ and \
last_dispid not in self._dispid_to_get_ and \
last_dispid not in self._dispid_to_put_:
return last_dispid
def _invokeex_(self, dispid, lcid, wFlags, args, kwArgs, serviceProvider):
### note: lcid is being ignored...
if wFlags & DISPATCH_METHOD:
try:
funcname = self._dispid_to_func_[dispid]
except KeyError:
if not wFlags & DISPATCH_PROPERTYGET:
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found
else:
try:
func = getattr(self._obj_, funcname)
except AttributeError:
# May have a dispid, but that doesnt mean we have the function!
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)
# Should check callable here
try:
return func(*args)
except TypeError as v:
# Particularly nasty is "wrong number of args" type error
# This helps you see what 'func' and 'args' actually is
if str(v).find("arguments")>=0:
print("** TypeError %s calling function %r(%r)" % (v, func, args))
raise
if wFlags & DISPATCH_PROPERTYGET:
try:
name = self._dispid_to_get_[dispid]
except KeyError:
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found
retob = getattr(self._obj_, name)
if type(retob)==types.MethodType: # a method as a property - call it.
retob = retob(*args)
return retob
if wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF): ### correct?
try:
name = self._dispid_to_put_[dispid]
except KeyError:
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # read-only
# If we have a method of that name (ie, a property get function), and
# we have an equiv. property set function, use that instead.
if type(getattr(self._obj_, name, None)) == types.MethodType and \
type(getattr(self._obj_, "Set" + name, None)) == types.MethodType:
fn = getattr(self._obj_, "Set" + name)
fn( *args )
else:
# just set the attribute
setattr(self._obj_, name, args[0])
return
raise COMException(scode=winerror.E_INVALIDARG, desc="invalid wFlags")
class EventHandlerPolicy(DesignatedWrapPolicy):
"""The default policy used by event handlers in the win32com.client package.
In addition to the base policy, this provides argument conversion semantics for
params
* dispatch params are converted to dispatch objects.
* Unicode objects are converted to strings (1.5.2 and earlier)
NOTE: Later, we may allow the object to override this process??
"""
def _transform_args_(self, args, kwArgs, dispid, lcid, wFlags, serviceProvider):
ret = []
for arg in args:
arg_type = type(arg)
if arg_type == IDispatchType:
import win32com.client
arg = win32com.client.Dispatch(arg)
elif arg_type == IUnknownType:
try:
import win32com.client
arg = win32com.client.Dispatch(arg.QueryInterface(pythoncom.IID_IDispatch))
except pythoncom.error:
pass # Keep it as IUnknown
ret.append(arg)
return tuple(ret), kwArgs
def _invokeex_(self, dispid, lcid, wFlags, args, kwArgs, serviceProvider):
# transform the args.
args, kwArgs = self._transform_args_(args, kwArgs, dispid, lcid, wFlags, serviceProvider)
return DesignatedWrapPolicy._invokeex_( self, dispid, lcid, wFlags, args, kwArgs, serviceProvider)
class DynamicPolicy(BasicWrapPolicy):
"""A policy which dynamically (ie, at run-time) determines public interfaces.
A dynamic policy is used to dynamically dispatch methods and properties to the
wrapped object. The list of objects and properties does not need to be known in
advance, and methods or properties added to the wrapped object after construction
are also handled.
The wrapped object must provide the following attributes:
_dynamic_ -- A method that will be called whenever an invoke on the object
is called. The method is called with the name of the underlying method/property
(ie, the mapping of dispid to/from name has been resolved.) This name property
may also be '_value_' to indicate the default, and '_NewEnum' to indicate a new
enumerator is requested.
"""
def _wrap_(self, object):
BasicWrapPolicy._wrap_(self, object)
if not hasattr(self._obj_, '_dynamic_'):
raise error("Object does not support Dynamic COM Policy")
self._next_dynamic_ = self._min_dynamic_ = 1000
self._dyn_dispid_to_name_ = {DISPID_VALUE:'_value_', DISPID_NEWENUM:'_NewEnum' }
def _getdispid_(self, name, fdex):
# TODO - Look at fdex flags.
lname = name.lower()
try:
return self._name_to_dispid_[lname]
except KeyError:
dispid = self._next_dynamic_ = self._next_dynamic_ + 1
self._name_to_dispid_[lname] = dispid
self._dyn_dispid_to_name_[dispid] = name # Keep case in this map...
return dispid
def _invoke_(self, dispid, lcid, wFlags, args):
return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)
def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
### note: lcid is being ignored...
### note: kwargs is being ignored...
### note: serviceProvider is being ignored...
### there might be assigned DISPID values to properties, too...
try:
name = self._dyn_dispid_to_name_[dispid]
except KeyError:
raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
return self._obj_._dynamic_(name, lcid, wFlags, args)
DefaultPolicy = DesignatedWrapPolicy
def resolve_func(spec):
"""Resolve a function by name
Given a function specified by 'module.function', return a callable object
(ie, the function itself)
"""
try:
idx = spec.rindex(".")
mname = spec[:idx]
fname = spec[idx+1:]
# Dont attempt to optimize by looking in sys.modules,
# as another thread may also be performing the import - this
# way we take advantage of the built-in import lock.
module = _import_module(mname)
return getattr(module, fname)
except ValueError: # No "." in name - assume in this module
return globals()[spec]
def call_func(spec, *args):
"""Call a function specified by name.
Call a function specified by 'module.function' and return the result.
"""
return resolve_func(spec)(*args)
def _import_module(mname):
"""Import a module just like the 'import' statement.
Having this function is much nicer for importing arbitrary modules than
using the 'exec' keyword. It is more efficient and obvious to the reader.
"""
__import__(mname)
# Eeek - result of _import_ is "win32com" - not "win32com.a.b.c"
# Get the full module from sys.modules
return sys.modules[mname]
#######
#
# Temporary hacks until all old code moves.
#
# These have been moved to a new source file, but some code may
# still reference them here. These will end up being removed.
try:
from .dispatcher import DispatcherTrace, DispatcherWin32trace
except ImportError: # Quite likely a frozen executable that doesnt need dispatchers
pass

View file

@ -0,0 +1,617 @@
"""Utilities for registering objects.
This module contains utility functions to register Python objects as
valid COM Servers. The RegisterServer function provides all information
necessary to allow the COM framework to respond to a request for a COM object,
construct the necessary Python object, and dispatch COM events.
"""
import sys
import win32api
import win32con
import pythoncom
import winerror
import os
CATID_PythonCOMServer = "{B3EF80D0-68E2-11D0-A689-00C04FD658FF}"
def _set_subkeys(keyName, valueDict, base=win32con.HKEY_CLASSES_ROOT):
hkey = win32api.RegCreateKey(base, keyName)
try:
for key, value in valueDict.items():
win32api.RegSetValueEx(hkey, key, None, win32con.REG_SZ, value)
finally:
win32api.RegCloseKey(hkey)
def _set_string(path, value, base=win32con.HKEY_CLASSES_ROOT):
"Set a string value in the registry."
win32api.RegSetValue(base,
path,
win32con.REG_SZ,
value)
def _get_string(path, base=win32con.HKEY_CLASSES_ROOT):
"Get a string value from the registry."
try:
return win32api.RegQueryValue(base, path)
except win32api.error:
return None
def _remove_key(path, base=win32con.HKEY_CLASSES_ROOT):
"Remove a string from the registry."
try:
win32api.RegDeleteKey(base, path)
except win32api.error as xxx_todo_changeme1:
(code, fn, msg) = xxx_todo_changeme1.args
if code != winerror.ERROR_FILE_NOT_FOUND:
raise win32api.error(code, fn, msg)
def recurse_delete_key(path, base=win32con.HKEY_CLASSES_ROOT):
"""Recursively delete registry keys.
This is needed since you can't blast a key when subkeys exist.
"""
try:
h = win32api.RegOpenKey(base, path)
except win32api.error as xxx_todo_changeme2:
(code, fn, msg) = xxx_todo_changeme2.args
if code != winerror.ERROR_FILE_NOT_FOUND:
raise win32api.error(code, fn, msg)
else:
# parent key found and opened successfully. do some work, making sure
# to always close the thing (error or no).
try:
# remove all of the subkeys
while 1:
try:
subkeyname = win32api.RegEnumKey(h, 0)
except win32api.error as xxx_todo_changeme:
(code, fn, msg) = xxx_todo_changeme.args
if code != winerror.ERROR_NO_MORE_ITEMS:
raise win32api.error(code, fn, msg)
break
recurse_delete_key(path + '\\' + subkeyname, base)
# remove the parent key
_remove_key(path, base)
finally:
win32api.RegCloseKey(h)
def _cat_registrar():
return pythoncom.CoCreateInstance(
pythoncom.CLSID_StdComponentCategoriesMgr,
None,
pythoncom.CLSCTX_INPROC_SERVER,
pythoncom.IID_ICatRegister
)
def _find_localserver_exe(mustfind):
if not sys.platform.startswith("win32"):
return sys.executable
if pythoncom.__file__.find("_d") < 0:
exeBaseName = "pythonw.exe"
else:
exeBaseName = "pythonw_d.exe"
# First see if in the same directory as this .EXE
exeName = os.path.join( os.path.split(sys.executable)[0], exeBaseName )
if not os.path.exists(exeName):
# See if in our sys.prefix directory
exeName = os.path.join( sys.prefix, exeBaseName )
if not os.path.exists(exeName):
# See if in our sys.prefix/pcbuild directory (for developers)
if "64 bit" in sys.version:
exeName = os.path.join( sys.prefix, "PCbuild", "amd64", exeBaseName )
else:
exeName = os.path.join( sys.prefix, "PCbuild", exeBaseName )
if not os.path.exists(exeName):
# See if the registry has some info.
try:
key = "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % sys.winver
path = win32api.RegQueryValue( win32con.HKEY_LOCAL_MACHINE, key )
exeName = os.path.join( path, exeBaseName )
except (AttributeError,win32api.error):
pass
if not os.path.exists(exeName):
if mustfind:
raise RuntimeError("Can not locate the program '%s'" % exeBaseName)
return None
return exeName
def _find_localserver_module():
import win32com.server
path = win32com.server.__path__[0]
baseName = "localserver"
pyfile = os.path.join(path, baseName + ".py")
try:
os.stat(pyfile)
except os.error:
# See if we have a compiled extension
if __debug__:
ext = ".pyc"
else:
ext = ".pyo"
pyfile = os.path.join(path, baseName + ext)
try:
os.stat(pyfile)
except os.error:
raise RuntimeError("Can not locate the Python module 'win32com.server.%s'" % baseName)
return pyfile
def RegisterServer(clsid,
pythonInstString=None,
desc=None,
progID=None, verProgID=None,
defIcon=None,
threadingModel="both",
policy=None,
catids=[], other={},
addPyComCat=None,
dispatcher = None,
clsctx = None,
addnPath = None,
):
"""Registers a Python object as a COM Server. This enters almost all necessary
information in the system registry, allowing COM to use the object.
clsid -- The (unique) CLSID of the server.
pythonInstString -- A string holding the instance name that will be created
whenever COM requests a new object.
desc -- The description of the COM object.
progID -- The user name of this object (eg, Word.Document)
verProgId -- The user name of this version's implementation (eg Word.6.Document)
defIcon -- The default icon for the object.
threadingModel -- The threading model this object supports.
policy -- The policy to use when creating this object.
catids -- A list of category ID's this object belongs in.
other -- A dictionary of extra items to be registered.
addPyComCat -- A flag indicating if the object should be added to the list
of Python servers installed on the machine. If None (the default)
then it will be registered when running from python source, but
not registered if running in a frozen environment.
dispatcher -- The dispatcher to use when creating this object.
clsctx -- One of the CLSCTX_* constants.
addnPath -- An additional path the COM framework will add to sys.path
before attempting to create the object.
"""
### backwards-compat check
### Certain policies do not require a "class name", just the policy itself.
if not pythonInstString and not policy:
raise TypeError('You must specify either the Python Class or Python Policy which implement the COM object.')
keyNameRoot = "CLSID\\%s" % str(clsid)
_set_string(keyNameRoot, desc)
# Also register as an "Application" so DCOM etc all see us.
_set_string("AppID\\%s" % clsid, progID)
# Depending on contexts requested, register the specified server type.
# Set default clsctx.
if not clsctx:
clsctx = pythoncom.CLSCTX_INPROC_SERVER | pythoncom.CLSCTX_LOCAL_SERVER
# And if we are frozen, ignore the ones that don't make sense in this
# context.
if pythoncom.frozen:
assert sys.frozen, "pythoncom is frozen, but sys.frozen is not set - don't know the context!"
if sys.frozen == "dll":
clsctx = clsctx & pythoncom.CLSCTX_INPROC_SERVER
else:
clsctx = clsctx & pythoncom.CLSCTX_LOCAL_SERVER
# Now setup based on the clsctx left over.
if clsctx & pythoncom.CLSCTX_INPROC_SERVER:
# get the module to use for registration.
# nod to Gordon's installer - if sys.frozen and sys.frozendllhandle
# exist, then we are being registered via a DLL - use this DLL as the
# file name.
if pythoncom.frozen:
if hasattr(sys, "frozendllhandle"):
dllName = win32api.GetModuleFileName(sys.frozendllhandle)
else:
raise RuntimeError("We appear to have a frozen DLL, but I don't know the DLL to use")
else:
# Normal case - running from .py file, so register pythoncom's DLL.
# Although now we prefer a 'loader' DLL if it exists to avoid some
# manifest issues (the 'loader' DLL has a manifest, but pythoncom does not)
pythoncom_dir = os.path.dirname(pythoncom.__file__)
if pythoncom.__file__.find("_d") < 0:
suffix = ""
else:
suffix = "_d"
loadername = "pythoncomloader%d%d%s.dll" % (sys.version_info[0], sys.version_info[1], suffix)
if os.path.isfile(os.path.join(pythoncom_dir, loadername)):
dllName = loadername
else:
# just use pythoncom.
dllName = os.path.basename(pythoncom.__file__)
_set_subkeys(keyNameRoot + "\\InprocServer32",
{ None : dllName,
"ThreadingModel" : threadingModel,
})
else: # Remove any old InProcServer32 registrations
_remove_key(keyNameRoot + "\\InprocServer32")
if clsctx & pythoncom.CLSCTX_LOCAL_SERVER:
if pythoncom.frozen:
# If we are frozen, we write "{exe} /Automate", just
# like "normal" .EXEs do
exeName = win32api.GetShortPathName(sys.executable)
command = '%s /Automate' % (exeName,)
else:
# Running from .py sources - we need to write
# 'python.exe win32com\server\localserver.py {clsid}"
exeName = _find_localserver_exe(1)
exeName = win32api.GetShortPathName(exeName)
pyfile = _find_localserver_module()
command = '%s "%s" %s' % (exeName, pyfile, str(clsid))
_set_string(keyNameRoot + '\\LocalServer32', command)
else: # Remove any old LocalServer32 registrations
_remove_key(keyNameRoot + "\\LocalServer32")
if pythonInstString:
_set_string(keyNameRoot + '\\PythonCOM', pythonInstString)
else:
_remove_key(keyNameRoot + '\\PythonCOM')
if policy:
_set_string(keyNameRoot + '\\PythonCOMPolicy', policy)
else:
_remove_key(keyNameRoot + '\\PythonCOMPolicy')
if dispatcher:
_set_string(keyNameRoot + '\\PythonCOMDispatcher', dispatcher)
else:
_remove_key(keyNameRoot + '\\PythonCOMDispatcher')
if defIcon:
_set_string(keyNameRoot + '\\DefaultIcon', defIcon)
else:
_remove_key(keyNameRoot + '\\DefaultIcon')
if addnPath:
_set_string(keyNameRoot + "\\PythonCOMPath", addnPath)
else:
_remove_key(keyNameRoot + "\\PythonCOMPath")
if addPyComCat is None:
addPyComCat = pythoncom.frozen == 0
if addPyComCat:
catids = catids + [ CATID_PythonCOMServer ]
# Set up the implemented categories
if catids:
regCat = _cat_registrar()
regCat.RegisterClassImplCategories(clsid, catids)
# set up any other reg values they might have
if other:
for key, value in other.items():
_set_string(keyNameRoot + '\\' + key, value)
if progID:
# set the progID as the most specific that was given to us
if verProgID:
_set_string(keyNameRoot + '\\ProgID', verProgID)
else:
_set_string(keyNameRoot + '\\ProgID', progID)
# Set up the root entries - version independent.
if desc:
_set_string(progID, desc)
_set_string(progID + '\\CLSID', str(clsid))
# Set up the root entries - version dependent.
if verProgID:
# point from independent to the current version
_set_string(progID + '\\CurVer', verProgID)
# point to the version-independent one
_set_string(keyNameRoot + '\\VersionIndependentProgID', progID)
# set up the versioned progID
if desc:
_set_string(verProgID, desc)
_set_string(verProgID + '\\CLSID', str(clsid))
def GetUnregisterServerKeys(clsid, progID=None, verProgID=None, customKeys = None):
"""Given a server, return a list of of ("key", root), which are keys recursively
and uncondtionally deleted at unregister or uninstall time.
"""
# remove the main CLSID registration
ret = [("CLSID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT)]
# remove the versioned ProgID registration
if verProgID:
ret.append((verProgID, win32con.HKEY_CLASSES_ROOT))
# blow away the independent ProgID. we can't leave it since we just
# torched the class.
### could potentially check the CLSID... ?
if progID:
ret.append((progID, win32con.HKEY_CLASSES_ROOT))
# The DCOM config tool may write settings to the AppID key for our CLSID
ret.append( ("AppID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT) )
# Any custom keys?
if customKeys:
ret = ret + customKeys
return ret
def UnregisterServer(clsid, progID=None, verProgID=None, customKeys = None):
"""Unregisters a Python COM server."""
for args in GetUnregisterServerKeys(clsid, progID, verProgID, customKeys ):
recurse_delete_key(*args)
### it might be nice at some point to "roll back" the independent ProgID
### to an earlier version if one exists, and just blowing away the
### specified version of the ProgID (and its corresponding CLSID)
### another time, though...
### NOTE: ATL simply blows away the above three keys without the
### potential checks that I describe. Assuming that defines the
### "standard" then we have no additional changes necessary.
def GetRegisteredServerOption(clsid, optionName):
"""Given a CLSID for a server and option name, return the option value
"""
keyNameRoot = "CLSID\\%s\\%s" % (str(clsid), str(optionName))
return _get_string(keyNameRoot)
def _get(ob, attr, default=None):
try:
return getattr(ob, attr)
except AttributeError:
pass
# look down sub-classes
try:
bases = ob.__bases__
except AttributeError:
# ob is not a class - no probs.
return default
for base in bases:
val = _get(base, attr, None)
if val is not None:
return val
return default
def RegisterClasses(*classes, **flags):
quiet = 'quiet' in flags and flags['quiet']
debugging = 'debug' in flags and flags['debug']
for cls in classes:
clsid = cls._reg_clsid_
progID = _get(cls, '_reg_progid_')
desc = _get(cls, '_reg_desc_', progID)
spec = _get(cls, '_reg_class_spec_')
verProgID = _get(cls, '_reg_verprogid_')
defIcon = _get(cls, '_reg_icon_')
threadingModel = _get(cls, '_reg_threading_', 'both')
catids = _get(cls, '_reg_catids_', [])
options = _get(cls, '_reg_options_', {})
policySpec = _get(cls, '_reg_policy_spec_')
clsctx = _get(cls, '_reg_clsctx_')
tlb_filename = _get(cls, '_reg_typelib_filename_')
# default to being a COM category only when not frozen.
addPyComCat = not _get(cls, '_reg_disable_pycomcat_', pythoncom.frozen!=0)
addnPath = None
if debugging:
# If the class has a debugging dispatcher specified, use it, otherwise
# use our default dispatcher.
dispatcherSpec = _get(cls, '_reg_debug_dispatcher_spec_')
if dispatcherSpec is None:
dispatcherSpec = "win32com.server.dispatcher.DefaultDebugDispatcher"
# And remember the debugging flag as servers may wish to use it at runtime.
debuggingDesc = "(for debugging)"
options['Debugging'] = "1"
else:
dispatcherSpec = _get(cls, '_reg_dispatcher_spec_')
debuggingDesc = ""
options['Debugging'] = "0"
if spec is None:
moduleName = cls.__module__
if moduleName == '__main__':
# Use argv[0] to determine the module name.
try:
# Use the win32api to find the case-sensitive name
moduleName = os.path.splitext(win32api.FindFiles(sys.argv[0])[0][8])[0]
except (IndexError, win32api.error):
# Can't find the script file - the user must explicitely set the _reg_... attribute.
raise TypeError("Can't locate the script hosting the COM object - please set _reg_class_spec_ in your object")
spec = moduleName + "." + cls.__name__
# Frozen apps don't need their directory on sys.path
if not pythoncom.frozen:
scriptDir = os.path.split(sys.argv[0])[0]
if not scriptDir: scriptDir = "."
addnPath = win32api.GetFullPathName(scriptDir)
RegisterServer(clsid, spec, desc, progID, verProgID, defIcon,
threadingModel, policySpec, catids, options,
addPyComCat, dispatcherSpec, clsctx, addnPath)
if not quiet:
print('Registered:', progID or spec, debuggingDesc)
# Register the typelibrary
if tlb_filename:
tlb_filename = os.path.abspath(tlb_filename)
typelib = pythoncom.LoadTypeLib(tlb_filename)
pythoncom.RegisterTypeLib(typelib, tlb_filename)
if not quiet:
print('Registered type library:', tlb_filename)
extra = flags.get('finalize_register')
if extra:
extra()
def UnregisterClasses(*classes, **flags):
quiet = 'quiet' in flags and flags['quiet']
for cls in classes:
clsid = cls._reg_clsid_
progID = _get(cls, '_reg_progid_')
verProgID = _get(cls, '_reg_verprogid_')
customKeys = _get(cls, '_reg_remove_keys_')
unregister_typelib = _get(cls, '_reg_typelib_filename_') is not None
UnregisterServer(clsid, progID, verProgID, customKeys)
if not quiet:
print('Unregistered:', progID or str(clsid))
if unregister_typelib:
tlb_guid = _get(cls, "_typelib_guid_")
if tlb_guid is None:
# I guess I could load the typelib, but they need the GUID anyway.
print("Have typelib filename, but no GUID - can't unregister")
else:
major, minor = _get(cls, "_typelib_version_", (1,0))
lcid = _get(cls, "_typelib_lcid_", 0)
try:
pythoncom.UnRegisterTypeLib(tlb_guid, major, minor, lcid)
if not quiet:
print('Unregistered type library')
except pythoncom.com_error:
pass
extra = flags.get('finalize_unregister')
if extra:
extra()
#
# Unregister info is for installers or external uninstallers.
# The WISE installer, for example firstly registers the COM server,
# then queries for the Unregister info, appending it to its
# install log. Uninstalling the package will the uninstall the server
def UnregisterInfoClasses(*classes, **flags):
ret = []
for cls in classes:
clsid = cls._reg_clsid_
progID = _get(cls, '_reg_progid_')
verProgID = _get(cls, '_reg_verprogid_')
customKeys = _get(cls, '_reg_remove_keys_')
ret = ret + GetUnregisterServerKeys(clsid, progID, verProgID, customKeys)
return ret
# Attempt to 're-execute' our current process with elevation.
def ReExecuteElevated(flags):
from win32com.shell.shell import ShellExecuteEx
from win32com.shell import shellcon
import win32process, win32event
import winxpgui # we've already checked we are running XP above
import tempfile
if not flags['quiet']:
print("Requesting elevation and retrying...")
new_params = " ".join(['"' + a + '"' for a in sys.argv])
# If we aren't already in unattended mode, we want our sub-process to
# be.
if not flags['unattended']:
new_params += " --unattended"
# specifying the parent means the dialog is centered over our window,
# which is a good usability clue.
# hwnd is unlikely on the command-line, but flags may come from elsewhere
hwnd = flags.get('hwnd', None)
if hwnd is None:
try:
hwnd = winxpgui.GetConsoleWindow()
except winxpgui.error:
hwnd = 0
# Redirect output so we give the user some clue what went wrong. This
# also means we need to use COMSPEC. However, the "current directory"
# appears to end up ignored - so we execute things via a temp batch file.
tempbase = tempfile.mktemp("pycomserverreg")
outfile = tempbase + ".out"
batfile = tempbase + ".bat"
# If registering from pythonwin, need to run python console instead since
# pythonwin will just open script for editting
current_exe = os.path.split(sys.executable)[1].lower()
exe_to_run = None
if current_exe == 'pythonwin.exe':
exe_to_run = os.path.join(sys.prefix, 'python.exe')
elif current_exe == 'pythonwin_d.exe':
exe_to_run = os.path.join(sys.prefix, 'python_d.exe')
if not exe_to_run or not os.path.exists(exe_to_run):
exe_to_run = sys.executable
try:
batf = open(batfile, "w")
try:
cwd = os.getcwd()
print("@echo off", file=batf)
# nothing is 'inherited' by the elevated process, including the
# environment. I wonder if we need to set more?
print("set PYTHONPATH=%s" % os.environ.get('PYTHONPATH', ''), file=batf)
# may be on a different drive - select that before attempting to CD.
print(os.path.splitdrive(cwd)[0], file=batf)
print('cd "%s"' % os.getcwd(), file=batf)
print('%s %s > "%s" 2>&1' % (win32api.GetShortPathName(exe_to_run), new_params, outfile), file=batf)
finally:
batf.close()
executable = os.environ.get('COMSPEC', 'cmd.exe')
rc = ShellExecuteEx(hwnd=hwnd,
fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
lpVerb="runas",
lpFile=executable,
lpParameters='/C "%s"' % batfile,
nShow=win32con.SW_SHOW)
hproc = rc['hProcess']
win32event.WaitForSingleObject(hproc, win32event.INFINITE)
exit_code = win32process.GetExitCodeProcess(hproc)
outf = open(outfile)
try:
output = outf.read()
finally:
outf.close()
if exit_code:
# Even if quiet you get to see this message.
print("Error: registration failed (exit code %s)." % exit_code)
# if we are quiet then the output if likely to already be nearly
# empty, so always print it.
print(output, end=' ')
finally:
for f in (outfile, batfile):
try:
os.unlink(f)
except os.error as exc:
print("Failed to remove tempfile '%s': %s" % (f, exc))
def UseCommandLine(*classes, **flags):
unregisterInfo = '--unregister_info' in sys.argv
unregister = '--unregister' in sys.argv
flags['quiet'] = flags.get('quiet',0) or '--quiet' in sys.argv
flags['debug'] = flags.get('debug',0) or '--debug' in sys.argv
flags['unattended'] = flags.get('unattended',0) or '--unattended' in sys.argv
if unregisterInfo:
return UnregisterInfoClasses(*classes, **flags)
try:
if unregister:
UnregisterClasses(*classes, **flags)
else:
RegisterClasses(*classes, **flags)
except win32api.error as exc:
# If we are on xp+ and have "access denied", retry using
# ShellExecuteEx with 'runas' verb to force elevation (vista) and/or
# admin login dialog (vista/xp)
if flags['unattended'] or exc.winerror != winerror.ERROR_ACCESS_DENIED \
or sys.getwindowsversion()[0] < 5:
raise
ReExecuteElevated(flags)
def RegisterPyComCategory():
""" Register the Python COM Server component category.
"""
regCat = _cat_registrar()
regCat.RegisterCategories( [ (CATID_PythonCOMServer,
0x0409,
"Python COM Server") ] )
if not pythoncom.frozen:
try:
win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
'Component Categories\\%s' % CATID_PythonCOMServer)
except win32api.error:
try:
RegisterPyComCategory()
except pythoncom.error: # Error with the COM category manager - oh well.
pass

View file

@ -0,0 +1,220 @@
""" General Server side utilities
"""
import pythoncom
from . import policy
import winerror
from .exception import COMException
def wrap(ob, iid=None, usePolicy=None, useDispatcher=None):
"""Wraps an object in a PyGDispatch gateway.
Returns a client side PyI{iid} interface.
Interface and gateway support must exist for the specified IID, as
the QueryInterface() method is used.
"""
if usePolicy is None:
usePolicy = policy.DefaultPolicy
if useDispatcher == 1: # True will also work here.
import win32com.server.dispatcher
useDispatcher = win32com.server.dispatcher.DefaultDebugDispatcher
if useDispatcher is None or useDispatcher==0:
ob = usePolicy(ob)
else:
ob = useDispatcher(usePolicy, ob)
# get a PyIDispatch, which interfaces to PyGDispatch
ob = pythoncom.WrapObject(ob)
if iid is not None:
ob = ob.QueryInterface(iid) # Ask the PyIDispatch if it supports it?
return ob
def unwrap(ob):
"""Unwraps an interface.
Given an interface which wraps up a Gateway, return the object behind
the gateway.
"""
ob = pythoncom.UnwrapObject(ob)
# see if the object is a dispatcher
if hasattr(ob, 'policy'):
ob = ob.policy
return ob._obj_
class ListEnumerator:
"""A class to expose a Python sequence as an EnumVARIANT.
Create an instance of this class passing a sequence (list, tuple, or
any sequence protocol supporting object) and it will automatically
support the EnumVARIANT interface for the object.
See also the @NewEnum@ function, which can be used to turn the
instance into an actual COM server.
"""
_public_methods_ = [ 'Next', 'Skip', 'Reset', 'Clone' ]
def __init__(self, data, index=0, iid = pythoncom.IID_IEnumVARIANT):
self._list_ = data
self.index = index
self._iid_ = iid
def _query_interface_(self, iid):
if iid == self._iid_:
return 1
def Next(self, count):
result = self._list_[self.index:self.index+count]
self.Skip(count)
return result
def Skip(self, count):
end = self.index + count
if end > len(self._list_):
end = len(self._list_)
self.index = end
def Reset(self):
self.index = 0
def Clone(self):
return self._wrap(self.__class__(self._list_, self.index))
def _wrap(self, ob):
return wrap(ob)
class ListEnumeratorGateway(ListEnumerator):
"""A List Enumerator which wraps a sequence's items in gateways.
If a sequence contains items (objects) that have not been wrapped for
return through the COM layers, then a ListEnumeratorGateway can be
used to wrap those items before returning them (from the Next() method).
See also the @ListEnumerator@ class and the @NewEnum@ function.
"""
def Next(self, count):
result = self._list_[self.index:self.index+count]
self.Skip(count)
return map(self._wrap, result)
def NewEnum(seq,
cls=ListEnumerator,
iid=pythoncom.IID_IEnumVARIANT,
usePolicy=None,
useDispatcher=None):
"""Creates a new enumerator COM server.
This function creates a new COM Server that implements the
IID_IEnumVARIANT interface.
A COM server that can enumerate the passed in sequence will be
created, then wrapped up for return through the COM framework.
Optionally, a custom COM server for enumeration can be passed
(the default is @ListEnumerator@), and the specific IEnum
interface can be specified.
"""
ob = cls(seq, iid=iid)
return wrap(ob, iid, usePolicy=usePolicy, useDispatcher=useDispatcher)
class Collection:
"A collection of VARIANT values."
_public_methods_ = [ 'Item', 'Count', 'Add', 'Remove', 'Insert' ]
def __init__(self, data=None, readOnly=0):
if data is None:
data = [ ]
self.data = data
# disable Add/Remove if read-only. note that we adjust _public_methods_
# on this instance only.
if readOnly:
self._public_methods_ = [ 'Item', 'Count' ]
# This method is also used as the "default" method.
# Thus "print ob" will cause this to be called with zero
# params. Handle this slightly more elegantly here.
# Ideally the policy should handle this.
def Item(self, *args):
if len(args) != 1:
raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
try:
return self.data[args[0]]
except IndexError as desc:
raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc))
_value_ = Item
def Count(self):
return len(self.data)
def Add(self, value):
self.data.append(value)
def Remove(self, index):
try:
del self.data[index]
except IndexError as desc:
raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc))
def Insert(self, index, value):
try:
index = int(index)
except (ValueError, TypeError):
raise COMException(scode=winerror.DISP_E_TYPEMISMATCH)
self.data.insert(index, value)
def _NewEnum(self):
return NewEnum(self.data)
def NewCollection(seq, cls=Collection):
"""Creates a new COM collection object
This function creates a new COM Server that implements the
common collection protocols, including enumeration. (_NewEnum)
A COM server that can enumerate the passed in sequence will be
created, then wrapped up for return through the COM framework.
Optionally, a custom COM server for enumeration can be passed
(the default is @Collection@).
"""
return pythoncom.WrapObject(policy.DefaultPolicy(cls(seq)),
pythoncom.IID_IDispatch,
pythoncom.IID_IDispatch)
class FileStream:
_public_methods_ = [ 'Read', 'Write', 'Clone', 'CopyTo', 'Seek' ]
_com_interfaces_ = [ pythoncom.IID_IStream ]
def __init__(self, file):
self.file = file
def Read(self, amount):
return self.file.read(amount)
def Write(self, data):
self.file.write(data)
return len(data)
def Clone(self):
return self._wrap(self.__class__(self.file))
def CopyTo(self, dest, cb):
data=self.file.read(cb)
cbread=len(data)
dest.Write(data) ## ??? Write does not currently return the length ???
return cbread, cbread
def Seek(self, offset, origin):
# how convient that the 'origin' values are the same as the CRT :)
self.file.seek(offset, origin)
return self.file.tell()
def _wrap(self, ob):
return wrap(ob)