Last active
August 8, 2016 19:11
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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