Skip to content

Instantly share code, notes, and snippets.

@markddavidoff
Last active August 8, 2016 19:11
Show Gist options
  • Save markddavidoff/4f0f22e225d0520cfe18 to your computer and use it in GitHub Desktop.
Save markddavidoff/4f0f22e225d0520cfe18 to your computer and use it in GitHub Desktop.
Comtypes wrapper to create an instance of a COM object with IClassFactory2 for objects that require licensing
# based on https://gist.github.com/EBNull/4219140
from uuid import UUID
from comtypes import GUID, IUnknown, CLSCTX_SERVER
from ctypes import OleDLL, WinDLL, c_ulong, byref, WINFUNCTYPE, \
POINTER, c_char_p, c_void_p
from ctypes.wintypes import HRESULT
IID_IClassFactory2 = "{B196B28F-BAB4-101A-B69C-00AA00341D07}"
def _raw_guid(guid):
"""Given a string GUID, return the GUID laid out in memory suitable for passing to ctypes"""
return UUID(str(guid)).bytes_le
# WINFUNCTYPE returns a prototype object which is then used to create
# CreateInstanceLic class below
proto_icf2_base = WINFUNCTYPE(HRESULT,
c_ulong,
c_ulong,
c_char_p,
c_ulong,
POINTER(c_void_p),
)
IClassFactory2__CreateInstanceLic = proto_icf2_base(7, 'CreateInstanceLic', (
# https://docs.python.org/2/library/ctypes.html#ctypes.WINFUNCTYPE
# in winfunctype tuples,
# 1 indicates Specifies an input parameter to the function.
# 2 indicates Output parameter. The foreign function fills in a value.
# 4 indicates a Input parameter which defaults to the integer zero.
# often you'll see ex: (1|4, 'pUnkReserved') to allow optional params
(1, 'pUnkOuter'),
(1, 'pUnkReserved'),
(1, 'riid'),
(1, 'bstrKey'),
(2, 'ppvObj'),
), _raw_guid(IID_IClassFactory2))
def CoReleaseObject(obj_ptr):
"""Calls Release() on a COM object. obj_ptr should be a c_void_p"""
if not obj_ptr:
return
IUnknown__Release = WINFUNCTYPE(HRESULT)(2, 'Release', (), IUnknown._iid_)
IUnknown__Release(obj_ptr)
def CoCreateInstanceLicenced(clsid_class, key, interface, dwClsContext=CLSCTX_SERVER):
"""
Uses IClassFactory2::CreateInstanceLic to create a COM object given a
licence key.
"""
ole = OleDLL("Ole32.dll")
clsid_class_raw = _raw_guid(clsid_class)
iclassfactory2 = _raw_guid(IID_IClassFactory2)
com_classfactory = c_void_p(0)
# does this:
# CoGetClassObject(CLSID_IberFuncs, CLSCTX_LOCAL_SERVER, NULL,
# IID_IClassFactory2, (LPVOID*)&classFactory);
ole.CoGetClassObject(clsid_class_raw, dwClsContext, None, iclassfactory2, byref(com_classfactory))
try:
iptr = CoCreateInstanceFromFactoryLicenced(
factory_ptr = com_classfactory,
key=key,
interface=interface,
pUnkOuter=None,
)
return iptr
finally:
# Release Factory reference
if com_classfactory:
CoReleaseObject(com_classfactory)
def CoCreateInstanceFromFactoryLicenced(factory_ptr, key, interface, pUnkOuter=None):
"""
Given a factory_ptr whose interface is IClassFactory2, create the instance
of clsid_class with the specified interface
does this:
classFactory-> CreateInstanceLic(NULL, NULL,
IID_IIberFuncs23, bstrKey, (PVOID*)iberFuncsInterface);
"""
interface_iid = str(interface._iid_)
requested_iid = _raw_guid(interface_iid)
ole_aut = WinDLL("OleAut32.dll")
key_bstr = ole_aut.SysAllocString(unicode(key))
try:
obj_addr = IClassFactory2__CreateInstanceLic(factory_ptr, 0, 0,
c_char_p(requested_iid), key_bstr)
# IClassFactory2__CreateInstanceLic returns the address of
# the interface pointer of the requested interface (requested_iid)
# The pointer will be of the type of the interface
# we need to create an interface pointer type class
interface_pointer_cls = POINTER(interface)
# then use that to convert the pointer addr into a pointer object
interface_pointer = interface_pointer_cls(obj_addr)
# now we have a pointer to our interface class!
return interface_pointer
finally:
if key_bstr:
ole_aut.SysFreeString(key_bstr)
def CreateLicensedObject(progid, interface, key):
"""
Comtypes wrapper for creating Objects which need to be licensed.
:param progid: str: name of COM object you want to instantiate
What Object you want to create. This must be
a registered Comtypes object
:param interface: comtypes class: comtypes wrapped interface object you want
to use to bind the com object. One way to get this is to load a registered
library, such as with comtypes.GetModule and then passing
the method
:param key: string: key to be used to license the object
:return: comtypes class: A comtypes wrapped pointer to instantiated
interface is returned. You can call this as you would a normal python method
e.g. func.GetEnum(...)
"""
clsid = GUID.from_progid(progid)
obj = CoCreateInstanceLicenced(str(clsid), key, interface)
return obj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment