You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1391 lines
52 KiB
1391 lines
52 KiB
import types
|
|
import sys
|
|
import os
|
|
|
|
# comtypes version numbers follow semver (http://semver.org/) and PEP 440
|
|
__version__ = "1.1.7"
|
|
|
|
import logging
|
|
class NullHandler(logging.Handler):
|
|
"""A Handler that does nothing."""
|
|
def emit(self, record):
|
|
pass
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Add a NULL handler to the comtypes logger. This prevents getting a
|
|
# message like this:
|
|
# No handlers could be found for logger "comtypes"
|
|
# when logging is not configured and logger.error() is called.
|
|
logger.addHandler(NullHandler())
|
|
|
|
from ctypes import *
|
|
from _ctypes import COMError
|
|
from comtypes import patcher
|
|
|
|
def _check_version(actual):
|
|
from comtypes.tools.codegenerator import version as required
|
|
if actual != required:
|
|
raise ImportError("Wrong version")
|
|
if not hasattr(sys, "frozen"):
|
|
g = sys._getframe(1).f_globals
|
|
mod_path = g.get("__file__")
|
|
tlb_path = g.get("typelib_path")
|
|
try:
|
|
mod_mtime = os.stat(mod_path).st_mtime
|
|
tlib_mtime = os.stat(tlb_path).st_mtime
|
|
except (OSError, TypeError):
|
|
return
|
|
if mod_mtime < tlib_mtime:
|
|
raise ImportError("Typelib newer than module")
|
|
|
|
try:
|
|
COMError()
|
|
except TypeError:
|
|
pass
|
|
else:
|
|
# Python 2.5 and 2.5.1 have a bug in the COMError implementation:
|
|
# The type has no __init__ method, and no hresult, text, and
|
|
# details instance vars. Work around this bug by monkeypatching
|
|
# COMError.
|
|
def monkeypatch_COMError():
|
|
def __init__(self, hresult, text, details):
|
|
self.hresult = hresult
|
|
self.text = text
|
|
self.details = details
|
|
super(COMError, self).__init__(hresult, text, details)
|
|
COMError.__init__ = __init__
|
|
monkeypatch_COMError()
|
|
del monkeypatch_COMError
|
|
|
|
if sys.version_info >= (3, 0):
|
|
pythonapi.PyInstanceMethod_New.argtypes = [py_object]
|
|
pythonapi.PyInstanceMethod_New.restype = py_object
|
|
PyInstanceMethod_Type = type(pythonapi.PyInstanceMethod_New(id))
|
|
|
|
def instancemethod(func, inst, cls):
|
|
mth = PyInstanceMethod_Type(func)
|
|
if inst is None:
|
|
return mth
|
|
return mth.__get__(inst)
|
|
else:
|
|
def instancemethod(func, inst, cls):
|
|
return types.MethodType(func, inst, cls)
|
|
|
|
class ReturnHRESULT(Exception):
|
|
"""ReturnHRESULT(hresult, text)
|
|
|
|
Return a hresult code from a COM method implementation
|
|
without logging an error.
|
|
"""
|
|
|
|
##class IDLWarning(UserWarning):
|
|
## "Warn about questionable type information"
|
|
|
|
from comtypes.GUID import GUID
|
|
_GUID = GUID
|
|
IID = GUID
|
|
DWORD = c_ulong
|
|
|
|
wireHWND = c_ulong
|
|
|
|
################################################################
|
|
# About COM apartments:
|
|
# http://blogs.msdn.com/larryosterman/archive/2004/04/28/122240.aspx
|
|
################################################################
|
|
|
|
################################################################
|
|
# constants for object creation
|
|
CLSCTX_INPROC_SERVER = 1
|
|
CLSCTX_INPROC_HANDLER = 2
|
|
CLSCTX_LOCAL_SERVER = 4
|
|
|
|
CLSCTX_INPROC = 3
|
|
CLSCTX_SERVER = 5
|
|
CLSCTX_ALL = 7
|
|
|
|
CLSCTX_INPROC_SERVER16 = 8
|
|
CLSCTX_REMOTE_SERVER = 16
|
|
CLSCTX_INPROC_HANDLER16 = 32
|
|
CLSCTX_RESERVED1 = 64
|
|
CLSCTX_RESERVED2 = 128
|
|
CLSCTX_RESERVED3 = 256
|
|
CLSCTX_RESERVED4 = 512
|
|
CLSCTX_NO_CODE_DOWNLOAD = 1024
|
|
CLSCTX_RESERVED5 = 2048
|
|
CLSCTX_NO_CUSTOM_MARSHAL = 4096
|
|
CLSCTX_ENABLE_CODE_DOWNLOAD = 8192
|
|
CLSCTX_NO_FAILURE_LOG = 16384
|
|
CLSCTX_DISABLE_AAA = 32768
|
|
CLSCTX_ENABLE_AAA = 65536
|
|
CLSCTX_FROM_DEFAULT_CONTEXT = 131072
|
|
|
|
tagCLSCTX = c_int # enum
|
|
CLSCTX = tagCLSCTX
|
|
|
|
# Constants for security setups
|
|
SEC_WINNT_AUTH_IDENTITY_UNICODE = 0x2
|
|
RPC_C_AUTHN_WINNT = 10
|
|
RPC_C_AUTHZ_NONE = 0
|
|
RPC_C_AUTHN_LEVEL_CONNECT = 2
|
|
RPC_C_IMP_LEVEL_IMPERSONATE = 3
|
|
EOAC_NONE = 0
|
|
|
|
|
|
|
|
################################################################
|
|
# Initialization and shutdown
|
|
_ole32 = oledll.ole32
|
|
_ole32_nohresult = windll.ole32 # use this for functions that don't return a HRESULT
|
|
|
|
COINIT_MULTITHREADED = 0x0
|
|
COINIT_APARTMENTTHREADED = 0x2
|
|
COINIT_DISABLE_OLE1DDE = 0x4
|
|
COINIT_SPEED_OVER_MEMORY = 0x8
|
|
|
|
def CoInitialize():
|
|
return CoInitializeEx(COINIT_APARTMENTTHREADED)
|
|
|
|
def CoInitializeEx(flags=None):
|
|
if flags is None:
|
|
if os.name == "ce":
|
|
flags = getattr(sys, "coinit_flags", COINIT_MULTITHREADED)
|
|
else:
|
|
flags = getattr(sys, "coinit_flags", COINIT_APARTMENTTHREADED)
|
|
logger.debug("CoInitializeEx(None, %s)", flags)
|
|
_ole32.CoInitializeEx(None, flags)
|
|
|
|
# COM is initialized automatically for the thread that imports this
|
|
# module for the first time. sys.coinit_flags is passed as parameter
|
|
# to CoInitializeEx, if defined, otherwise COINIT_APARTMENTTHREADED
|
|
# (COINIT_MULTITHREADED on Windows CE) is used.
|
|
#
|
|
# A shutdown function is registered with atexit, so that
|
|
# CoUninitialize is called when Python is shut down.
|
|
CoInitializeEx()
|
|
|
|
# We need to have CoUninitialize for multithreaded model where we have
|
|
# to initialize and uninitialize COM for every new thread (except main)
|
|
# in which we are using COM
|
|
def CoUninitialize():
|
|
logger.debug("CoUninitialize()")
|
|
_ole32_nohresult.CoUninitialize()
|
|
|
|
|
|
def shutdown(func=_ole32_nohresult.CoUninitialize,
|
|
_debug=logger.debug,
|
|
_exc_clear=getattr(sys, "exc_clear", lambda: None)):
|
|
# Make sure no COM pointers stay in exception frames.
|
|
_exc_clear()
|
|
# Sometimes, CoUnititialize, running at Python shutdown,
|
|
# raises an exception. We suppress this when __debug__ is
|
|
# False.
|
|
_debug("Calling CoUnititialize()")
|
|
if __debug__:
|
|
func()
|
|
else:
|
|
try: func()
|
|
except WindowsError: pass
|
|
# Set the flag which means that calling obj.Release() is no longer
|
|
# needed.
|
|
if _cominterface_meta is not None:
|
|
_cominterface_meta._com_shutting_down = True
|
|
_debug("CoUnititialize() done.")
|
|
|
|
import atexit
|
|
atexit.register(shutdown)
|
|
del shutdown
|
|
|
|
################################################################
|
|
# global registries.
|
|
|
|
# allows to find interface classes by guid strings (iid)
|
|
com_interface_registry = {}
|
|
|
|
# allows to find coclasses by guid strings (clsid)
|
|
com_coclass_registry = {}
|
|
|
|
def _is_object(obj):
|
|
"""This function determines if the argument is a COM object. It
|
|
is used in several places to determine whether propputref or
|
|
propput setters have to be used."""
|
|
from comtypes.automation import VARIANT
|
|
# A COM pointer is an 'Object'
|
|
if isinstance(obj, POINTER(IUnknown)):
|
|
return True
|
|
# A COM pointer in a VARIANT is an 'Object', too
|
|
elif isinstance(obj, VARIANT) and isinstance(obj.value, POINTER(IUnknown)):
|
|
return True
|
|
# It may be a dynamic dispatch object.
|
|
return hasattr(obj, "_comobj")
|
|
|
|
################################################################
|
|
# The metaclasses...
|
|
|
|
class _cominterface_meta(type):
|
|
"""Metaclass for COM interfaces. Automatically creates high level
|
|
methods from COMMETHOD lists.
|
|
"""
|
|
|
|
# This flag is set to True by the atexit handler which calls
|
|
# CoUnititialize.
|
|
_com_shutting_down = False
|
|
|
|
# Creates also a POINTER type for the newly created class.
|
|
def __new__(self, name, bases, namespace):
|
|
methods = namespace.pop("_methods_", None)
|
|
dispmethods = namespace.pop("_disp_methods_", None)
|
|
cls = type.__new__(self, name, bases, namespace)
|
|
|
|
if methods is not None:
|
|
cls._methods_ = methods
|
|
if dispmethods is not None:
|
|
cls._disp_methods_ = dispmethods
|
|
|
|
# If we sublass a COM interface, for example:
|
|
#
|
|
# class IDispatch(IUnknown):
|
|
# ....
|
|
#
|
|
# then we need to make sure that POINTER(IDispatch) is a
|
|
# subclass of POINTER(IUnknown) because of the way ctypes
|
|
# typechecks work.
|
|
if bases == (object,):
|
|
_ptr_bases = (cls, _compointer_base)
|
|
else:
|
|
_ptr_bases = (cls, POINTER(bases[0]))
|
|
|
|
# The interface 'cls' is used as a mixin.
|
|
p = type(_compointer_base)("POINTER(%s)" % cls.__name__,
|
|
_ptr_bases,
|
|
{"__com_interface__": cls,
|
|
"_needs_com_addref_": None})
|
|
|
|
from ctypes import _pointer_type_cache
|
|
_pointer_type_cache[cls] = p
|
|
|
|
if cls._case_insensitive_:
|
|
|
|
@patcher.Patch(p)
|
|
class CaseInsensitive(object):
|
|
# case insensitive attributes for COM methods and properties
|
|
def __getattr__(self, name):
|
|
"""Implement case insensitive access to methods and properties"""
|
|
try:
|
|
fixed_name = self.__map_case__[name.lower()]
|
|
except KeyError:
|
|
raise AttributeError(name)
|
|
if fixed_name != name: # prevent unbounded recursion
|
|
return getattr(self, fixed_name)
|
|
raise AttributeError(name)
|
|
|
|
# __setattr__ is pretty heavy-weight, because it is called for
|
|
# EVERY attribute assignment. Settings a non-com attribute
|
|
# through this function takes 8.6 usec, while without this
|
|
# function it takes 0.7 sec - 12 times slower.
|
|
#
|
|
# How much faster would this be if implemented in C?
|
|
def __setattr__(self, name, value):
|
|
"""Implement case insensitive access to methods and properties"""
|
|
object.__setattr__(self,
|
|
self.__map_case__.get(name.lower(), name),
|
|
value)
|
|
|
|
@patcher.Patch(POINTER(p))
|
|
class ReferenceFix(object):
|
|
def __setitem__(self, index, value):
|
|
# We override the __setitem__ method of the
|
|
# POINTER(POINTER(interface)) type, so that the COM
|
|
# reference count is managed correctly.
|
|
#
|
|
# This is so that we can implement COM methods that have to
|
|
# return COM pointers more easily and consistent. Instead of
|
|
# using CopyComPointer in the method implementation, we can
|
|
# simply do:
|
|
#
|
|
# def GetTypeInfo(self, this, ..., pptinfo):
|
|
# if not pptinfo: return E_POINTER
|
|
# pptinfo[0] = a_com_interface_pointer
|
|
# return S_OK
|
|
if index != 0:
|
|
# CopyComPointer, which is in _ctypes, does only
|
|
# handle an index of 0. This code does what
|
|
# CopyComPointer should do if index != 0.
|
|
if bool(value):
|
|
value.AddRef()
|
|
super(POINTER(p), self).__setitem__(index, value)
|
|
return
|
|
from _ctypes import CopyComPointer
|
|
CopyComPointer(value, self)
|
|
|
|
return cls
|
|
|
|
def __setattr__(self, name, value):
|
|
if name == "_methods_":
|
|
# XXX I'm no longer sure why the code generator generates
|
|
# "_methods_ = []" in the interface definition, and later
|
|
# overrides this by "Interface._methods_ = [...]
|
|
## assert self.__dict__.get("_methods_", None) is None
|
|
self._make_methods(value)
|
|
self._make_specials()
|
|
elif name == "_disp_methods_":
|
|
assert self.__dict__.get("_disp_methods_", None) is None
|
|
self._make_dispmethods(value)
|
|
self._make_specials()
|
|
type.__setattr__(self, name, value)
|
|
|
|
def _make_specials(self):
|
|
# This call installs methods that forward the Python protocols
|
|
# to COM protocols.
|
|
|
|
def has_name(name):
|
|
# Determine whether a property or method named 'name'
|
|
# exists
|
|
if self._case_insensitive_:
|
|
return name.lower() in self.__map_case__
|
|
return hasattr(self, name)
|
|
|
|
# XXX These special methods should be generated by the code generator.
|
|
if has_name("Count"):
|
|
@patcher.Patch(self)
|
|
class _(object):
|
|
def __len__(self):
|
|
"Return the the 'self.Count' property."
|
|
return self.Count
|
|
|
|
if has_name("Item"):
|
|
@patcher.Patch(self)
|
|
class _(object):
|
|
# 'Item' is the 'default' value. Make it available by
|
|
# calling the instance (Not sure this makes sense, but
|
|
# win32com does this also).
|
|
def __call__(self, *args, **kw):
|
|
"Return 'self.Item(*args, **kw)'"
|
|
return self.Item(*args, **kw)
|
|
|
|
# does this make sense? It seems that all standard typelibs I've
|
|
# seen so far that support .Item also support ._NewEnum
|
|
@patcher.no_replace
|
|
def __getitem__(self, index):
|
|
"Return 'self.Item(index)'"
|
|
# Handle tuples and all-slice
|
|
if isinstance(index, tuple):
|
|
args = index
|
|
elif index == _all_slice:
|
|
args = ()
|
|
else:
|
|
args = (index,)
|
|
|
|
try:
|
|
result = self.Item(*args)
|
|
except COMError as err:
|
|
(hresult, text, details) = err.args
|
|
if hresult == -2147352565: # DISP_E_BADINDEX
|
|
raise IndexError("invalid index")
|
|
else:
|
|
raise
|
|
|
|
# Note that result may be NULL COM pointer. There is no way
|
|
# to interpret this properly, so it is returned as-is.
|
|
|
|
# Hm, should we call __ctypes_from_outparam__ on the
|
|
# result?
|
|
return result
|
|
|
|
@patcher.no_replace
|
|
def __setitem__(self, index, value):
|
|
"Attempt 'self.Item[index] = value'"
|
|
try:
|
|
self.Item[index] = value
|
|
except COMError as err:
|
|
(hresult, text, details) = err.args
|
|
if hresult == -2147352565: # DISP_E_BADINDEX
|
|
raise IndexError("invalid index")
|
|
else:
|
|
raise
|
|
except TypeError:
|
|
msg = "%r object does not support item assignment"
|
|
raise TypeError(msg % type(self))
|
|
|
|
if has_name("_NewEnum"):
|
|
@patcher.Patch(self)
|
|
class _(object):
|
|
def __iter__(self):
|
|
"Return an iterator over the _NewEnum collection."
|
|
# This method returns a pointer to _some_ _NewEnum interface.
|
|
# It relies on the fact that the code generator creates next()
|
|
# methods for them automatically.
|
|
#
|
|
# Better would maybe to return an object that
|
|
# implements the Python iterator protocol, and
|
|
# forwards the calls to the COM interface.
|
|
enum = self._NewEnum
|
|
if isinstance(enum, types.MethodType):
|
|
# _NewEnum should be a propget property, with dispid -4.
|
|
#
|
|
# Sometimes, however, it is a method.
|
|
enum = enum()
|
|
if hasattr(enum, "Next"):
|
|
return enum
|
|
# _NewEnum returns an IUnknown pointer, QueryInterface() it to
|
|
# IEnumVARIANT
|
|
from comtypes.automation import IEnumVARIANT
|
|
return enum.QueryInterface(IEnumVARIANT)
|
|
|
|
def _make_case_insensitive(self):
|
|
# The __map_case__ dictionary maps lower case names to the
|
|
# names in the original spelling to enable case insensitive
|
|
# method and attribute access.
|
|
try:
|
|
self.__dict__["__map_case__"]
|
|
except KeyError:
|
|
d = {}
|
|
d.update(getattr(self, "__map_case__", {}))
|
|
self.__map_case__ = d
|
|
|
|
def _make_dispmethods(self, methods):
|
|
if self._case_insensitive_:
|
|
self._make_case_insensitive()
|
|
|
|
# create dispinterface methods and properties on the interface 'self'
|
|
properties = {}
|
|
for m in methods:
|
|
what, name, idlflags, restype, argspec = m
|
|
|
|
# is it a property set or property get?
|
|
is_prop = False
|
|
|
|
# argspec is a sequence of tuples, each tuple is:
|
|
# ([paramflags], type, name)
|
|
try:
|
|
memid = [x for x in idlflags if isinstance(x, int)][0]
|
|
except IndexError:
|
|
raise TypeError("no dispid found in idlflags")
|
|
if what == "DISPPROPERTY": # DISPPROPERTY
|
|
assert not argspec # XXX does not yet work for properties with parameters
|
|
accessor = self._disp_property(memid, idlflags)
|
|
is_prop = True
|
|
setattr(self, name, accessor)
|
|
elif what == "DISPMETHOD": # DISPMETHOD
|
|
# argspec is a tuple of (idlflags, type, name[,
|
|
# defval]) items.
|
|
method = self._disp_method(memid, name, idlflags, restype, argspec)
|
|
## not in 2.3 method.__name__ = name
|
|
if 'propget' in idlflags:
|
|
nargs = len(argspec)
|
|
properties.setdefault((name, nargs), [None, None, None])[0] = method
|
|
is_prop = True
|
|
elif 'propput' in idlflags:
|
|
nargs = len(argspec)-1
|
|
properties.setdefault((name, nargs), [None, None, None])[1] = method
|
|
is_prop = True
|
|
elif 'propputref' in idlflags:
|
|
nargs = len(argspec)-1
|
|
properties.setdefault((name, nargs), [None, None, None])[2] = method
|
|
is_prop = True
|
|
else:
|
|
setattr(self, name, method)
|
|
# COM is case insensitive.
|
|
#
|
|
# For a method, this is the real name. For a property,
|
|
# this is the name WITHOUT the _set_ or _get_ prefix.
|
|
if self._case_insensitive_:
|
|
self.__map_case__[name.lower()] = name
|
|
if is_prop:
|
|
self.__map_case__[name[5:].lower()] = name[5:]
|
|
|
|
for (name, nargs), methods in list(properties.items()):
|
|
# methods contains [propget or None, propput or None, propputref or None]
|
|
if methods[1] is not None and methods[2] is not None:
|
|
# both propput and propputref.
|
|
#
|
|
# Create a setter method that examines the argument type
|
|
# and calls 'propputref' if it is an Object (in the VB
|
|
# sense), or call 'propput' otherwise.
|
|
propput = methods[1]
|
|
propputref = methods[2]
|
|
def put_or_putref(self, *args):
|
|
if _is_object(args[-1]):
|
|
return propputref(self, *args)
|
|
else:
|
|
return propput(self, *args)
|
|
methods[1] = put_or_putref
|
|
del methods[2]
|
|
elif methods[2] is not None:
|
|
# use propputref
|
|
del methods[1]
|
|
else:
|
|
# use propput (if any)
|
|
del methods[2]
|
|
if nargs:
|
|
setattr(self, name, named_property("%s.%s" % (self.__name__, name), *methods))
|
|
else:
|
|
assert len(methods) <= 2
|
|
setattr(self, name, property(*methods))
|
|
|
|
# COM is case insensitive
|
|
if self._case_insensitive_:
|
|
self.__map_case__[name.lower()] = name
|
|
|
|
# Some ideas, (not only) related to disp_methods:
|
|
#
|
|
# Should the functions/methods we create have restype and/or
|
|
# argtypes attributes?
|
|
|
|
def _disp_method(self, memid, name, idlflags, restype, argspec):
|
|
if 'propget' in idlflags:
|
|
def getfunc(obj, *args, **kw):
|
|
return self.Invoke(obj, memid, _invkind=2, *args, **kw) # DISPATCH_PROPERTYGET
|
|
return getfunc
|
|
elif 'propput' in idlflags:
|
|
def putfunc(obj, *args, **kw):
|
|
return self.Invoke(obj, memid, _invkind=4, *args, **kw) # DISPATCH_PROPERTYPUT
|
|
return putfunc
|
|
elif 'propputref' in idlflags:
|
|
def putfunc(obj, *args, **kw):
|
|
return self.Invoke(obj, memid, _invkind=8, *args, **kw) # DISPATCH_PROPERTYPUTREF
|
|
return putfunc
|
|
# a first attempt to make use of the restype. Still, support
|
|
# for named arguments and default argument values should be
|
|
# added.
|
|
if hasattr(restype, "__com_interface__"):
|
|
interface = restype.__com_interface__
|
|
def func(s, *args, **kw):
|
|
result = self.Invoke(s, memid, _invkind=1, *args, **kw)
|
|
if result is None:
|
|
return
|
|
return result.QueryInterface(interface)
|
|
else:
|
|
def func(obj, *args, **kw):
|
|
return self.Invoke(obj, memid, _invkind=1, *args, **kw) # DISPATCH_METHOD
|
|
return func
|
|
|
|
def _disp_property(self, memid, idlflags):
|
|
# XXX doc string missing in property
|
|
def _get(obj):
|
|
return obj.Invoke(memid, _invkind=2) # DISPATCH_PROPERTYGET
|
|
if "readonly" in idlflags:
|
|
return property(_get)
|
|
def _set(obj, value):
|
|
# Detect whether to use DISPATCH_PROPERTYPUT or
|
|
# DISPATCH_PROPERTYPUTREF
|
|
invkind = 8 if _is_object(value) else 4
|
|
return obj.Invoke(memid, value, _invkind=invkind)
|
|
return property(_get, _set)
|
|
|
|
def __get_baseinterface_methodcount(self):
|
|
"Return the number of com methods in the base interfaces"
|
|
try:
|
|
result = 0
|
|
for itf in self.mro()[1:-1]:
|
|
result += len(itf.__dict__["_methods_"])
|
|
return result
|
|
except KeyError as err:
|
|
(name,) = err.args
|
|
if name == "_methods_":
|
|
raise TypeError("baseinterface '%s' has no _methods_" % itf.__name__)
|
|
raise
|
|
|
|
def _fix_inout_args(self, func, argtypes, paramflags):
|
|
# This function provides a workaround for a bug in ctypes.
|
|
# [in, out] parameters must be converted with the argtype's
|
|
# .from_param() method BEFORE they are passed to the _ctypes
|
|
# build_callargs() function in Modules/_ctypes/_ctypes.c.
|
|
#
|
|
# For details see below.
|
|
#
|
|
# TODO: The workaround should be disabled when a ctypes
|
|
# version is used where the bug is fixed.
|
|
SIMPLETYPE = type(c_int)
|
|
BYREFTYPE = type(byref(c_int()))
|
|
def call_with_inout(self_, *args, **kw):
|
|
args = list(args)
|
|
# Indexed by order in the output
|
|
outargs = {}
|
|
outnum = 0
|
|
for i, info in enumerate(paramflags):
|
|
direction = info[0]
|
|
if direction & 3 == 3:
|
|
# This is an [in, out] parameter.
|
|
#
|
|
# Determine name and required type of the parameter.
|
|
name = info[1]
|
|
# [in, out] parameters are passed as pointers,
|
|
# this is the pointed-to type:
|
|
atyp = argtypes[i]._type_
|
|
|
|
# Get the actual parameter, either as positional or
|
|
# keyword arg.
|
|
try:
|
|
try:
|
|
v = args[i]
|
|
except IndexError:
|
|
v = kw[name]
|
|
except KeyError:
|
|
# no parameter was passed, make an empty one
|
|
# of the required type
|
|
v = atyp()
|
|
else:
|
|
# parameter was passed, call .from_param() to
|
|
# convert it to a ctypes type.
|
|
if getattr(v, "_type_", None) is atyp:
|
|
# Array of or pointer to type 'atyp' was
|
|
# passed, pointer to 'atyp' expected.
|
|
pass
|
|
elif type(atyp) is SIMPLETYPE:
|
|
# The from_param method of simple types
|
|
# (c_int, c_double, ...) returns a byref()
|
|
# object which we cannot use since later
|
|
# it will be wrapped in a pointer. Simply
|
|
# call the constructor with the argument
|
|
# in that case.
|
|
v = atyp(v)
|
|
else:
|
|
v = atyp.from_param(v)
|
|
assert not isinstance(v, BYREFTYPE)
|
|
outargs[outnum] = v
|
|
outnum += 1
|
|
if len(args) > i:
|
|
args[i] = v
|
|
else:
|
|
kw[name] = v
|
|
elif direction & 2 == 2:
|
|
outnum += 1
|
|
|
|
rescode = func(self_, *args, **kw)
|
|
# If there is only a single output value, then do not expect it to
|
|
# be iterable.
|
|
if outnum == 1: # rescode is not iterable
|
|
if len(outargs) == 1:
|
|
rescode = rescode.__ctypes_from_outparam__()
|
|
return rescode
|
|
|
|
rescode = list(rescode)
|
|
for outnum, o in list(outargs.items()):
|
|
rescode[outnum] = o.__ctypes_from_outparam__()
|
|
return rescode
|
|
return call_with_inout
|
|
|
|
def _make_methods(self, methods):
|
|
if self._case_insensitive_:
|
|
self._make_case_insensitive()
|
|
|
|
# we insist on an _iid_ in THIS class!
|
|
try:
|
|
iid = self.__dict__["_iid_"]
|
|
except KeyError:
|
|
raise AttributeError("this class must define an _iid_")
|
|
else:
|
|
iid = str(iid)
|
|
## if iid in com_interface_registry:
|
|
## # Warn when multiple interfaces are defined with identical iids.
|
|
## # This would also trigger if we reload() a module that contains
|
|
## # interface types, so suppress the warning in this case.
|
|
## other = com_interface_registry[iid]
|
|
## if self.__name__ != other.__name__ or self.__module__ != other.__module__:
|
|
## text = "Multiple interface defn: %s, %s" % (self, other)
|
|
## warnings.warn(text, UserWarning)
|
|
com_interface_registry[iid] = self
|
|
del iid
|
|
vtbl_offset = self.__get_baseinterface_methodcount()
|
|
|
|
properties = {}
|
|
|
|
# create private low level, and public high level methods
|
|
for i, item in enumerate(methods):
|
|
restype, name, argtypes, paramflags, idlflags, doc = item
|
|
# the function prototype
|
|
prototype = WINFUNCTYPE(restype, *argtypes)
|
|
|
|
# a low level unbound method calling the com method.
|
|
# attach it with a private name (__com_AddRef, for example),
|
|
# so that custom method implementations can call it.
|
|
|
|
# If the method returns a HRESULT, we pass the interface iid,
|
|
# so that we can request error info for the interface.
|
|
if restype == HRESULT:
|
|
## print "%s.%s" % (self.__name__, name)
|
|
raw_func = prototype(i + vtbl_offset, name, None, self._iid_)
|
|
func = prototype(i + vtbl_offset, name, paramflags, self._iid_)
|
|
else:
|
|
raw_func = prototype(i + vtbl_offset, name, None, None)
|
|
func = prototype(i + vtbl_offset, name, paramflags, None)
|
|
setattr(self,
|
|
"_%s__com_%s" % (self.__name__, name),
|
|
instancemethod(raw_func, None, self))
|
|
|
|
if paramflags:
|
|
# see comment in the _fix_inout_args method
|
|
dirflags = [(p[0]&3) for p in paramflags]
|
|
if 3 in dirflags:
|
|
## fullname = "%s::%s" % (self.__name__, name)
|
|
## print "FIX %s" % fullname
|
|
func = self._fix_inout_args(func, argtypes, paramflags)
|
|
|
|
# 'func' is a high level function calling the COM method
|
|
func.__doc__ = doc
|
|
try:
|
|
func.__name__ = name # for pyhelp
|
|
except TypeError:
|
|
# In Python 2.3, __name__ is a readonly attribute
|
|
pass
|
|
# make it an unbound method. Remember, 'self' is a type here.
|
|
mth = instancemethod(func, None, self)
|
|
|
|
# is it a property set or property get?
|
|
is_prop = False
|
|
|
|
# XXX Hm. What, when paramflags is None?
|
|
# Or does have '0' values?
|
|
# Seems we loose then, at least for properties...
|
|
|
|
# The following code assumes that the docstrings for
|
|
# propget and propput are identical.
|
|
if "propget" in idlflags:
|
|
assert name.startswith("_get_")
|
|
nargs = len([flags for flags in paramflags
|
|
if flags[0] & 7 in (0, 1)])
|
|
# XXX or should we do this?
|
|
# nargs = len([flags for flags in paramflags
|
|
# if (flags[0] & 1) or (flags[0] == 0)])
|
|
propname = name[len("_get_"):]
|
|
properties.setdefault((propname, doc, nargs), [None, None, None])[0] = func
|
|
is_prop = True
|
|
elif "propput" in idlflags:
|
|
assert name.startswith("_set_")
|
|
nargs = len([flags for flags in paramflags
|
|
if flags[0] & 7 in (0, 1)]) - 1
|
|
propname = name[len("_set_"):]
|
|
properties.setdefault((propname, doc, nargs), [None, None, None])[1] = func
|
|
is_prop = True
|
|
elif "propputref" in idlflags:
|
|
assert name.startswith("_setref_")
|
|
nargs = len([flags for flags in paramflags
|
|
if flags[0] & 7 in (0, 1)]) - 1
|
|
propname = name[len("_setref_"):]
|
|
properties.setdefault((propname, doc, nargs), [None, None, None])[2] = func
|
|
is_prop = True
|
|
|
|
# We install the method in the class, except when it's a
|
|
# property accessor. And we make sure we don't overwrite
|
|
# a property that's already present in the class.
|
|
if not is_prop:
|
|
if hasattr(self, name):
|
|
setattr(self, "_" + name, mth)
|
|
else:
|
|
setattr(self, name, mth)
|
|
|
|
# COM is case insensitive.
|
|
#
|
|
# For a method, this is the real name. For a property,
|
|
# this is the name WITHOUT the _set_ or _get_ prefix.
|
|
if self._case_insensitive_:
|
|
self.__map_case__[name.lower()] = name
|
|
if is_prop:
|
|
self.__map_case__[name[5:].lower()] = name[5:]
|
|
|
|
# create public properties / attribute accessors
|
|
for (name, doc, nargs), methods in list(properties.items()):
|
|
# methods contains [propget or None, propput or None, propputref or None]
|
|
if methods[1] is not None and methods[2] is not None:
|
|
# both propput and propputref.
|
|
#
|
|
# Create a setter method that examines the argument type
|
|
# and calls 'propputref' if it is an Object (in the VB
|
|
# sense), or call 'propput' otherwise.
|
|
propput = methods[1]
|
|
propputref = methods[2]
|
|
def put_or_putref(self, *args):
|
|
if _is_object(args[-1]):
|
|
return propputref(self, *args)
|
|
else:
|
|
return propput(self, *args)
|
|
methods[1] = put_or_putref
|
|
del methods[2]
|
|
elif methods[2] is not None:
|
|
# use propputref
|
|
del methods[1]
|
|
else:
|
|
# use propput (if any)
|
|
del methods[2]
|
|
if nargs == 0:
|
|
prop = property(*methods + [None, doc])
|
|
else:
|
|
# Hm, must be a descriptor where the __get__ method
|
|
# returns a bound object having __getitem__ and
|
|
# __setitem__ methods.
|
|
prop = named_property("%s.%s" % (self.__name__, name), *methods + [doc])
|
|
# Again, we should not overwrite class attributes that are
|
|
# already present.
|
|
if hasattr(self, name):
|
|
setattr(self, "_" + name, prop)
|
|
else:
|
|
setattr(self, name, prop)
|
|
|
|
# COM is case insensitive
|
|
if self._case_insensitive_:
|
|
self.__map_case__[name.lower()] = name
|
|
|
|
|
|
################################################################
|
|
# helper classes for COM propget / propput
|
|
# Should they be implemented in C for speed?
|
|
|
|
_all_slice = slice(None, None, None)
|
|
|
|
|
|
class bound_named_property(object):
|
|
def __init__(self, name, getter, setter, im_inst):
|
|
self.name = name
|
|
self.im_inst = im_inst
|
|
self.getter = getter
|
|
self.setter = setter
|
|
|
|
def __getitem__(self, index):
|
|
if self.getter is None:
|
|
raise TypeError("unsubscriptable object")
|
|
if isinstance(index, tuple):
|
|
return self.getter(self.im_inst, *index)
|
|
elif index == _all_slice:
|
|
return self.getter(self.im_inst)
|
|
else:
|
|
return self.getter(self.im_inst, index)
|
|
|
|
def __call__(self, *args):
|
|
if self.getter is None:
|
|
raise TypeError("object is not callable")
|
|
return self.getter(self.im_inst, *args)
|
|
|
|
def __setitem__(self, index, value):
|
|
if self.setter is None:
|
|
raise TypeError("object does not support item assignment")
|
|
if isinstance(index, tuple):
|
|
self.setter(self.im_inst, *(index + (value,)))
|
|
elif index == _all_slice:
|
|
self.setter(self.im_inst, value)
|
|
else:
|
|
self.setter(self.im_inst, index, value)
|
|
|
|
def __repr__(self):
|
|
return "<bound_named_property %r at %x>" % (self.name, id(self))
|
|
|
|
def __iter__(self):
|
|
""" Explicitly disallow iteration. """
|
|
msg = "%r is not iterable" % self.name
|
|
raise TypeError(msg)
|
|
|
|
|
|
|
|
class named_property(object):
|
|
def __init__(self, name, fget=None, fset=None, doc=None):
|
|
self.name = name
|
|
self.getter = fget
|
|
self.setter = fset
|
|
self.__doc__ = doc
|
|
|
|
def __get__(self, im_inst, im_class=None):
|
|
if im_inst is None:
|
|
return self
|
|
return bound_named_property(self.name, self.getter, self.setter, im_inst)
|
|
|
|
# Make this a data descriptor
|
|
def __set__(self, obj):
|
|
raise AttributeError("Unsettable attribute")
|
|
|
|
def __repr__(self):
|
|
return "<named_property %r at %x>" % (self.name, id(self))
|
|
|
|
################################################################
|
|
|
|
class _compointer_meta(type(c_void_p), _cominterface_meta):
|
|
"metaclass for COM interface pointer classes"
|
|
# no functionality, but needed to avoid a metaclass conflict
|
|
|
|
class _compointer_base(c_void_p, metaclass=_compointer_meta):
|
|
"base class for COM interface pointer classes"
|
|
def __del__(self, _debug=logger.debug):
|
|
"Release the COM refcount we own."
|
|
if self:
|
|
# comtypes calls CoUnititialize() when the atexit handlers
|
|
# runs. CoUninitialize() cleans up the COM objects that
|
|
# are still alive. Python COM pointers may still be
|
|
# present but we can no longer call Release() on them -
|
|
# this may give a protection fault. So we need the
|
|
# _com_shutting_down flag.
|
|
#
|
|
if not type(self)._com_shutting_down:
|
|
_debug("Release %s", self)
|
|
self.Release()
|
|
|
|
def __cmp__(self, other):
|
|
"""Compare pointers to COM interfaces."""
|
|
# COM identity rule
|
|
#
|
|
# XXX To compare COM interface pointers, should we
|
|
# automatically QueryInterface for IUnknown on both items, and
|
|
# compare the pointer values?
|
|
if not isinstance(other, _compointer_base):
|
|
return 1
|
|
|
|
# get the value property of the c_void_p baseclass, this is the pointer value
|
|
return cmp(super(_compointer_base, self).value, super(_compointer_base, other).value)
|
|
|
|
def __eq__(self, other):
|
|
if not isinstance(other, _compointer_base):
|
|
return False
|
|
# get the value property of the c_void_p baseclass, this is the pointer value
|
|
return super(_compointer_base, self).value == super(_compointer_base, other).value
|
|
|
|
def __hash__(self):
|
|
"""Return the hash value of the pointer."""
|
|
# hash the pointer values
|
|
return hash(super(_compointer_base, self).value)
|
|
|
|
# redefine the .value property; return the object itself.
|
|
def __get_value(self):
|
|
return self
|
|
value = property(__get_value, doc="""Return self.""")
|
|
|
|
def __repr__(self):
|
|
ptr = super(_compointer_base, self).value
|
|
return "<%s ptr=0x%x at %x>" % (self.__class__.__name__, ptr or 0, id(self))
|
|
|
|
# This fixes the problem when there are multiple python interface types
|
|
# wrapping the same COM interface. This could happen because some interfaces
|
|
# are contained in multiple typelibs.
|
|
#
|
|
# It also allows to pass a CoClass instance to an api
|
|
# expecting a COM interface.
|
|
def from_param(klass, value):
|
|
"""Convert 'value' into a COM pointer to the interface.
|
|
|
|
This method accepts a COM pointer, or a CoClass instance
|
|
which is QueryInterface()d."""
|
|
if value is None:
|
|
return None
|
|
# CLF: 2013-01-18
|
|
# A default value of 0, meaning null, can pass through to here.
|
|
if value == 0:
|
|
return None
|
|
if isinstance(value, klass):
|
|
return value
|
|
# multiple python interface types for the same COM interface.
|
|
# Do we need more checks here?
|
|
if klass._iid_ == getattr(value, "_iid_", None):
|
|
return value
|
|
# Accept an CoClass instance which exposes the interface required.
|
|
try:
|
|
table = value._com_pointers_
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
try:
|
|
# a kind of QueryInterface
|
|
return table[klass._iid_]
|
|
except KeyError:
|
|
raise TypeError("Interface %s not supported" % klass._iid_)
|
|
return value.QueryInterface(klass.__com_interface__)
|
|
from_param = classmethod(from_param)
|
|
|
|
################################################################
|
|
|
|
from ctypes import _SimpleCData
|
|
|
|
class BSTR(_SimpleCData):
|
|
"The windows BSTR data type"
|
|
_type_ = "X"
|
|
_needsfree = False
|
|
def __repr__(self):
|
|
return "%s(%r)" % (self.__class__.__name__, self.value)
|
|
|
|
def __ctypes_from_outparam__(self):
|
|
self._needsfree = True
|
|
return self.value
|
|
|
|
def __del__(self, _free=windll.oleaut32.SysFreeString):
|
|
# Free the string if self owns the memory
|
|
# or if instructed by __ctypes_from_outparam__.
|
|
if self._b_base_ is None \
|
|
or self._needsfree:
|
|
_free(self)
|
|
|
|
def from_param(cls, value):
|
|
"""Convert into a foreign function call parameter."""
|
|
if isinstance(value, cls):
|
|
return value
|
|
# Although the builtin SimpleCData.from_param call does the
|
|
# right thing, it doesn't ensure that SysFreeString is called
|
|
# on destruction.
|
|
return cls(value)
|
|
from_param = classmethod(from_param)
|
|
|
|
################################################################
|
|
# IDL stuff
|
|
|
|
class helpstring(str):
|
|
"Specifies the helpstring for a COM method or property."
|
|
|
|
class defaultvalue(object):
|
|
"Specifies the default value for parameters marked optional."
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
class dispid(int):
|
|
"Specifies the DISPID of a method or property."
|
|
|
|
# XXX STDMETHOD, COMMETHOD, DISPMETHOD, and DISPPROPERTY should return
|
|
# instances with methods, or at least accessors instead of tuple.
|
|
|
|
def STDMETHOD(restype, name, argtypes=()):
|
|
"Specifies a COM method slot without idlflags"
|
|
# restype, name, argtypes, paramflags, idlflags, docstring
|
|
return restype, name, argtypes, None, (), None
|
|
|
|
def DISPMETHOD(idlflags, restype, name, *argspec):
|
|
"Specifies a method of a dispinterface"
|
|
return "DISPMETHOD", name, idlflags, restype, argspec
|
|
|
|
def DISPPROPERTY(idlflags, proptype, name):
|
|
"Specifies a property of a dispinterface"
|
|
return "DISPPROPERTY", name, idlflags, proptype, ()#, argspec
|
|
|
|
# COMMETHOD returns:
|
|
# restype, methodname, tuple(argtypes), tuple(paramflags), tuple(idlflags), helptext
|
|
#
|
|
# paramflags is a sequence of (flags (integer), paramname (string)
|
|
# tuple(idlflags) is for the method itself: (dispid, 'readonly')
|
|
#
|
|
# Example: (HRESULT, 'Width', (c_long,), (2, 'rhs'), (4, 'readonly'), None)
|
|
|
|
## sample generated code:
|
|
## DISPPROPERTY([5, 'readonly'], OLE_YSIZE_HIMETRIC, 'Height'),
|
|
## DISPMETHOD([6], None, 'Render',
|
|
## ( [], c_int, 'hdc' ),
|
|
## ( [], c_int, 'x' ),
|
|
## ( [], c_int, 'y' ))
|
|
|
|
################################################################
|
|
|
|
_PARAMFLAGS = {
|
|
"in": 1,
|
|
"out": 2,
|
|
"lcid": 4,
|
|
"retval": 8,
|
|
"optional": 16,
|
|
}
|
|
|
|
def _encode_idl(names):
|
|
# sum up all values found in _PARAMFLAGS, ignoring all others.
|
|
return sum([_PARAMFLAGS.get(n, 0) for n in names])
|
|
|
|
_NOTHING = object()
|
|
def _unpack_argspec(idl, typ, name=None, defval=_NOTHING):
|
|
return idl, typ, name, defval
|
|
|
|
def COMMETHOD(idlflags, restype, methodname, *argspec):
|
|
"""Specifies a COM method slot with idlflags.
|
|
|
|
XXX should explain the sematics of the arguments.
|
|
"""
|
|
paramflags = []
|
|
argtypes = []
|
|
|
|
# collect all helpstring instances
|
|
# We should suppress docstrings when Python is started with -OO
|
|
helptext = [t for t in idlflags if isinstance(t, helpstring)]
|
|
# join them together(does this make sense?) and replace by None if empty.
|
|
helptext = "".join(helptext) or None
|
|
|
|
from comtypes.automation import VARIANT
|
|
|
|
for item in argspec:
|
|
idl, typ, argname, defval = _unpack_argspec(*item)
|
|
pflags = _encode_idl(idl)
|
|
if "optional" in idl:
|
|
if defval is _NOTHING:
|
|
if typ is VARIANT:
|
|
defval = VARIANT.missing
|
|
elif typ is POINTER(VARIANT):
|
|
defval = pointer(VARIANT.missing)
|
|
else:
|
|
## msg = ("'optional' only allowed for VARIANT and VARIANT*, not for %s"
|
|
## % typ.__name__)
|
|
## warnings.warn(msg, IDLWarning, stacklevel=2)
|
|
defval = typ()
|
|
if defval is _NOTHING:
|
|
paramflags.append((pflags, argname))
|
|
else:
|
|
paramflags.append((pflags, argname, defval))
|
|
argtypes.append(typ)
|
|
if "propget" in idlflags:
|
|
methodname = "_get_%s" % methodname
|
|
elif "propput" in idlflags:
|
|
methodname = "_set_%s" % methodname
|
|
elif "propputref" in idlflags:
|
|
methodname = "_setref_%s" % methodname
|
|
return restype, methodname, tuple(argtypes), tuple(paramflags), tuple(idlflags), helptext
|
|
|
|
################################################################
|
|
# IUnknown, the root of all evil...
|
|
|
|
class IUnknown(object, metaclass=_cominterface_meta):
|
|
"""The most basic COM interface.
|
|
|
|
Each subclasses of IUnknown must define these class attributes:
|
|
|
|
_iid_ - a GUID instance defining the identifier of this interface
|
|
|
|
_methods_ - a list of methods for this interface.
|
|
|
|
The _methods_ list must in VTable order. Methods are specified
|
|
with STDMETHOD or COMMETHOD calls.
|
|
"""
|
|
_case_insensitive_ = False
|
|
_iid_ = GUID("{00000000-0000-0000-C000-000000000046}")
|
|
|
|
_methods_ = [
|
|
STDMETHOD(HRESULT, "QueryInterface",
|
|
[POINTER(GUID), POINTER(c_void_p)]),
|
|
STDMETHOD(c_ulong, "AddRef"),
|
|
STDMETHOD(c_ulong, "Release")
|
|
]
|
|
|
|
def QueryInterface(self, interface, iid=None):
|
|
"QueryInterface(interface) -> instance"
|
|
p = POINTER(interface)()
|
|
if iid is None:
|
|
iid = interface._iid_
|
|
self.__com_QueryInterface(byref(iid), byref(p))
|
|
clsid = self.__dict__.get('__clsid')
|
|
if clsid is not None:
|
|
p.__dict__['__clsid'] = clsid
|
|
return p
|
|
|
|
# these are only so that they get a docstring.
|
|
# XXX There should be other ways to install a docstring.
|
|
def AddRef(self):
|
|
"Increase the internal refcount by one and return it."
|
|
return self.__com_AddRef()
|
|
|
|
def Release(self):
|
|
"Decrease the internal refcount by one and return it."
|
|
return self.__com_Release()
|
|
|
|
# IPersist is a trivial interface, which allows to ask an object about
|
|
# its clsid.
|
|
class IPersist(IUnknown):
|
|
_iid_ = GUID('{0000010C-0000-0000-C000-000000000046}')
|
|
_idlflags_ = []
|
|
_methods_ = [
|
|
COMMETHOD([], HRESULT, 'GetClassID',
|
|
( ['out'], POINTER(GUID), 'pClassID' )),
|
|
]
|
|
|
|
class IServiceProvider(IUnknown):
|
|
_iid_ = GUID('{6D5140C1-7436-11CE-8034-00AA006009FA}')
|
|
|
|
# Overridden QueryService to make it nicer to use (passing it an
|
|
# interface and it returns a pointer to that interface)
|
|
def QueryService(self, serviceIID, interface):
|
|
p = POINTER(interface)()
|
|
self._QueryService(byref(serviceIID), byref(interface._iid_), byref(p))
|
|
return p
|
|
|
|
_methods_ = [
|
|
COMMETHOD([], HRESULT, 'QueryService',
|
|
( ['in'], POINTER(GUID), 'guidService' ),
|
|
( ['in'], POINTER(GUID), 'riid' ),
|
|
( ['in'], POINTER(c_void_p), 'ppvObject' ))
|
|
]
|
|
|
|
################################################################
|
|
def CoGetObject(displayname, interface):
|
|
"""Convert a displayname to a moniker, then bind and return the object
|
|
identified by the moniker."""
|
|
if interface is None:
|
|
interface = IUnknown
|
|
punk = POINTER(interface)()
|
|
# Do we need a way to specify the BIND_OPTS parameter?
|
|
_ole32.CoGetObject(str(displayname),
|
|
None,
|
|
byref(interface._iid_),
|
|
byref(punk))
|
|
return punk
|
|
|
|
def CoCreateInstance(clsid, interface=None, clsctx=None, punkouter=None):
|
|
"""The basic windows api to create a COM class object and return a
|
|
pointer to an interface.
|
|
"""
|
|
if clsctx is None:
|
|
clsctx = CLSCTX_SERVER
|
|
if interface is None:
|
|
interface = IUnknown
|
|
p = POINTER(interface)()
|
|
iid = interface._iid_
|
|
_ole32.CoCreateInstance(byref(clsid), punkouter, clsctx, byref(iid), byref(p))
|
|
return p
|
|
|
|
def CoGetClassObject(clsid, clsctx=None, pServerInfo=None, interface=None):
|
|
if clsctx is None:
|
|
clsctx = CLSCTX_SERVER
|
|
if interface is None:
|
|
import comtypes.server
|
|
interface = comtypes.server.IClassFactory
|
|
p = POINTER(interface)()
|
|
_CoGetClassObject(clsid,
|
|
clsctx,
|
|
pServerInfo,
|
|
interface._iid_,
|
|
byref(p))
|
|
return p
|
|
|
|
def GetActiveObject(clsid, interface=None):
|
|
"""Retrieves a pointer to a running object"""
|
|
p = POINTER(IUnknown)()
|
|
oledll.oleaut32.GetActiveObject(byref(clsid), None, byref(p))
|
|
if interface is not None:
|
|
p = p.QueryInterface(interface)
|
|
return p
|
|
|
|
class MULTI_QI(Structure):
|
|
_fields_ = [("pIID", POINTER(GUID)),
|
|
("pItf", POINTER(c_void_p)),
|
|
("hr", HRESULT)]
|
|
|
|
class _COAUTHIDENTITY(Structure):
|
|
_fields_ = [
|
|
('User', POINTER(c_ushort)),
|
|
('UserLength', c_ulong),
|
|
('Domain', POINTER(c_ushort)),
|
|
('DomainLength', c_ulong),
|
|
('Password', POINTER(c_ushort)),
|
|
('PasswordLength', c_ulong),
|
|
('Flags', c_ulong),
|
|
]
|
|
COAUTHIDENTITY = _COAUTHIDENTITY
|
|
|
|
class _COAUTHINFO(Structure):
|
|
_fields_ = [
|
|
('dwAuthnSvc', c_ulong),
|
|
('dwAuthzSvc', c_ulong),
|
|
('pwszServerPrincName', c_wchar_p),
|
|
('dwAuthnLevel', c_ulong),
|
|
('dwImpersonationLevel', c_ulong),
|
|
('pAuthIdentityData', POINTER(_COAUTHIDENTITY)),
|
|
('dwCapabilities', c_ulong),
|
|
]
|
|
COAUTHINFO = _COAUTHINFO
|
|
|
|
class _COSERVERINFO(Structure):
|
|
_fields_ = [
|
|
('dwReserved1', c_ulong),
|
|
('pwszName', c_wchar_p),
|
|
('pAuthInfo', POINTER(_COAUTHINFO)),
|
|
('dwReserved2', c_ulong),
|
|
]
|
|
COSERVERINFO = _COSERVERINFO
|
|
_CoGetClassObject = _ole32.CoGetClassObject
|
|
_CoGetClassObject.argtypes = [POINTER(GUID), DWORD, POINTER(COSERVERINFO),
|
|
POINTER(GUID), POINTER(c_void_p)]
|
|
|
|
class tagBIND_OPTS(Structure):
|
|
_fields_ = [
|
|
('cbStruct', c_ulong),
|
|
('grfFlags', c_ulong),
|
|
('grfMode', c_ulong),
|
|
('dwTickCountDeadline', c_ulong)
|
|
]
|
|
# XXX Add __init__ which sets cbStruct?
|
|
BIND_OPTS = tagBIND_OPTS
|
|
|
|
class tagBIND_OPTS2(Structure):
|
|
_fields_ = [
|
|
('cbStruct', c_ulong),
|
|
('grfFlags', c_ulong),
|
|
('grfMode', c_ulong),
|
|
('dwTickCountDeadline', c_ulong),
|
|
('dwTrackFlags', c_ulong),
|
|
('dwClassContext', c_ulong),
|
|
('locale', c_ulong),
|
|
('pServerInfo', POINTER(_COSERVERINFO)),
|
|
]
|
|
# XXX Add __init__ which sets cbStruct?
|
|
BINDOPTS2 = tagBIND_OPTS2
|
|
|
|
#Structures for security setups
|
|
#########################################
|
|
class _SEC_WINNT_AUTH_IDENTITY(Structure):
|
|
_fields_ = [
|
|
('User', POINTER(c_ushort)),
|
|
('UserLength', c_ulong),
|
|
('Domain', POINTER(c_ushort)),
|
|
('DomainLength', c_ulong),
|
|
('Password', POINTER(c_ushort)),
|
|
('PasswordLength', c_ulong),
|
|
('Flags', c_ulong),
|
|
]
|
|
SEC_WINNT_AUTH_IDENTITY = _SEC_WINNT_AUTH_IDENTITY
|
|
|
|
class _SOLE_AUTHENTICATION_INFO(Structure):
|
|
_fields_ = [
|
|
('dwAuthnSvc', c_ulong),
|
|
('dwAuthzSvc', c_ulong),
|
|
('pAuthInfo', POINTER(_SEC_WINNT_AUTH_IDENTITY)),
|
|
]
|
|
SOLE_AUTHENTICATION_INFO = _SOLE_AUTHENTICATION_INFO
|
|
|
|
class _SOLE_AUTHENTICATION_LIST(Structure):
|
|
_fields_ = [
|
|
('cAuthInfo', c_ulong),
|
|
('pAuthInfo', POINTER(_SOLE_AUTHENTICATION_INFO)),
|
|
]
|
|
SOLE_AUTHENTICATION_LIST = _SOLE_AUTHENTICATION_LIST
|
|
|
|
def CoCreateInstanceEx(clsid, interface=None,
|
|
clsctx=None,
|
|
machine=None,
|
|
pServerInfo=None):
|
|
"""The basic windows api to create a COM class object and return a
|
|
pointer to an interface, possibly on another machine.
|
|
|
|
Passing both "machine" and "pServerInfo" results in a ValueError.
|
|
|
|
"""
|
|
if clsctx is None:
|
|
clsctx=CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER
|
|
|
|
if pServerInfo is not None:
|
|
if machine is not None:
|
|
msg = "Can not specify both machine name and server info"
|
|
raise ValueError(msg)
|
|
elif machine is not None:
|
|
serverinfo = COSERVERINFO()
|
|
serverinfo.pwszName = machine
|
|
pServerInfo = byref(serverinfo)
|
|
|
|
if interface is None:
|
|
interface = IUnknown
|
|
multiqi = MULTI_QI()
|
|
multiqi.pIID = pointer(interface._iid_)
|
|
_ole32.CoCreateInstanceEx(byref(clsid),
|
|
None,
|
|
clsctx,
|
|
pServerInfo,
|
|
1,
|
|
byref(multiqi))
|
|
return cast(multiqi.pItf, POINTER(interface))
|
|
|
|
|
|
################################################################
|
|
from comtypes._comobject import COMObject
|
|
|
|
# What's a coclass?
|
|
# a POINTER to a coclass is allowed as parameter in a function declaration:
|
|
# http://msdn.microsoft.com/library/en-us/midl/midl/oleautomation.asp
|
|
|
|
from comtypes._meta import _coclass_meta
|
|
|
|
class CoClass(COMObject, metaclass=_coclass_meta):
|
|
pass
|
|
################################################################
|