mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-14 17:22:56 -07:00
Bump cherrypy from 18.8.0 to 18.9.0 (#2266)
* Bump cherrypy from 18.8.0 to 18.9.0 Bumps [cherrypy](https://github.com/cherrypy/cherrypy) from 18.8.0 to 18.9.0. - [Changelog](https://github.com/cherrypy/cherrypy/blob/main/CHANGES.rst) - [Commits](https://github.com/cherrypy/cherrypy/compare/v18.8.0...v18.9.0) --- updated-dependencies: - dependency-name: cherrypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update cherrypy==18.9.0 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci]
This commit is contained in:
parent
cfefa928be
commit
faef9a94c4
673 changed files with 159850 additions and 11583 deletions
1
lib/win32com/server/__init__.py
Normal file
1
lib/win32com/server/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
# Empty __init__ file to designate a sub-package.
|
84
lib/win32com/server/connect.py
Normal file
84
lib/win32com/server/connect.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
"""Utilities for Server Side connections.
|
||||
|
||||
A collection of helpers for server side connection points.
|
||||
"""
|
||||
import pythoncom
|
||||
import win32com.server.util
|
||||
import winerror
|
||||
from win32com import olectl
|
||||
|
||||
from .exception import Exception
|
||||
|
||||
# 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)))
|
291
lib/win32com/server/dispatcher.py
Normal file
291
lib/win32com/server/dispatcher.py
Normal file
|
@ -0,0 +1,291 @@
|
|||
"""Dispatcher
|
||||
|
||||
Please see policy.py for a discussion on dispatchers and policies
|
||||
"""
|
||||
import traceback
|
||||
from sys import exc_info
|
||||
|
||||
import pythoncom
|
||||
import win32api
|
||||
import win32com
|
||||
|
||||
#
|
||||
from win32com.server.exception import IsCOMServerException
|
||||
from win32com.util import IIDToInterfaceName
|
||||
|
||||
|
||||
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
|
105
lib/win32com/server/exception.py
Normal file
105
lib/win32com/server/exception.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
"""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
|
||||
|
||||
import 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
|
26
lib/win32com/server/factory.py
Normal file
26
lib/win32com/server/factory.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# 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)
|
53
lib/win32com/server/localserver.py
Normal file
53
lib/win32com/server/localserver.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# 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()
|
829
lib/win32com/server/policy.py
Normal file
829
lib/win32com/server/policy.py
Normal file
|
@ -0,0 +1,829 @@
|
|||
"""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 sys
|
||||
import types
|
||||
|
||||
import pythoncom
|
||||
import pywintypes
|
||||
import win32api
|
||||
import win32con
|
||||
import winerror
|
||||
|
||||
# Import a few important constants to speed lookups.
|
||||
from pythoncom import (
|
||||
DISPATCH_METHOD,
|
||||
DISPATCH_PROPERTYGET,
|
||||
DISPATCH_PROPERTYPUT,
|
||||
DISPATCH_PROPERTYPUTREF,
|
||||
DISPID_COLLECT,
|
||||
DISPID_CONSTRUCTOR,
|
||||
DISPID_DESTRUCTOR,
|
||||
DISPID_EVALUATE,
|
||||
DISPID_NEWENUM,
|
||||
DISPID_PROPERTYPUT,
|
||||
DISPID_STARTENUM,
|
||||
DISPID_UNKNOWN,
|
||||
DISPID_VALUE,
|
||||
)
|
||||
|
||||
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
|
672
lib/win32com/server/register.py
Normal file
672
lib/win32com/server/register.py
Normal file
|
@ -0,0 +1,672 @@
|
|||
"""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 os
|
||||
import sys
|
||||
|
||||
import pythoncom
|
||||
import win32api
|
||||
import win32con
|
||||
import winerror
|
||||
|
||||
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__)
|
||||
suffix = "_d" if "_d" in pythoncom.__file__ else ""
|
||||
# Always register with the full path to the DLLs.
|
||||
loadername = os.path.join(
|
||||
pythoncom_dir,
|
||||
"pythoncomloader%d%d%s.dll"
|
||||
% (sys.version_info[0], sys.version_info[1], suffix),
|
||||
)
|
||||
dllName = loadername if os.path.isfile(loadername) else 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):
|
||||
import tempfile
|
||||
|
||||
import win32event # we've already checked we are running XP above
|
||||
import win32process
|
||||
import winxpgui
|
||||
from win32com.shell import shellcon
|
||||
from win32com.shell.shell import ShellExecuteEx
|
||||
|
||||
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
|
229
lib/win32com/server/util.py
Normal file
229
lib/win32com/server/util.py
Normal file
|
@ -0,0 +1,229 @@
|
|||
""" General Server side utilities
|
||||
"""
|
||||
import pythoncom
|
||||
import winerror
|
||||
|
||||
from . import policy
|
||||
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)
|
Loading…
Add table
Add a link
Reference in a new issue