Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
1
venv/Lib/site-packages/win32com/server/__init__.py
Normal file
1
venv/Lib/site-packages/win32com/server/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
# Empty __init__ file to designate a sub-package.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
65
venv/Lib/site-packages/win32com/server/connect.py
Normal file
65
venv/Lib/site-packages/win32com/server/connect.py
Normal 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)))
|
||||
|
||||
|
270
venv/Lib/site-packages/win32com/server/dispatcher.py
Normal file
270
venv/Lib/site-packages/win32com/server/dispatcher.py
Normal 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
|
91
venv/Lib/site-packages/win32com/server/exception.py
Normal file
91
venv/Lib/site-packages/win32com/server/exception.py
Normal 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
|
22
venv/Lib/site-packages/win32com/server/factory.py
Normal file
22
venv/Lib/site-packages/win32com/server/factory.py
Normal 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)
|
49
venv/Lib/site-packages/win32com/server/localserver.py
Normal file
49
venv/Lib/site-packages/win32com/server/localserver.py
Normal 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()
|
750
venv/Lib/site-packages/win32com/server/policy.py
Normal file
750
venv/Lib/site-packages/win32com/server/policy.py
Normal 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
|
617
venv/Lib/site-packages/win32com/server/register.py
Normal file
617
venv/Lib/site-packages/win32com/server/register.py
Normal 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
|
||||
|
220
venv/Lib/site-packages/win32com/server/util.py
Normal file
220
venv/Lib/site-packages/win32com/server/util.py
Normal 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)
|
Loading…
Add table
Add a link
Reference in a new issue