I've been trying to use the digi Advanced Device Discovery protocol library with python using ctypes.
the context:
Windows 7 x64
python 2.7.5
dll library
here's my current code:
guid = (0xbf6db409,0xc83d,0x44a3,0xa3,0x6d,0x21,0x79,0x7d,0x2f,0x73,0xf9)
class ADDP():
from ctypes import Structure
class GUID(Structure):
from ctypes.wintypes import DWORD,WORD,BYTE
_fields_ = [("Data1",DWORD),
("Data2",WORD),
("Data3",WORD),
("Data4",BYTE * 8)]
def __init__(self, guid):
from ctypes import windll, c_void_p, c_byte, pointer,c_char,POINTER
from ctypes.wintypes import HANDLE
import ctypes
self.dll = windll.LoadLibrary("D:\\Lib\\addp.dll")
self.guid = self.GUID()
self.guid.Data1 = guid[0]
self.guid.Data2 = guid[1]
self.guid.Data3 = guid[2]
self.guid.Data4 = (c_byte * 8)(guid[3],guid[4],guid[5],guid[6],guid[7],guid[8],guid[9],guid[10])
addpopen = self.dll[1]
addpopen.argtypes = [POINTER(self.GUID),]
addpopen.restype = c_void_p
#print addpopen.restype
self.handler = addpopen(pointer(self.guid))
if self.handler == None:
raise RuntimeError()
self.opened = False
else:
self.opened = True
def isOpen(self):
return self.opened
def Discover(self):
from ctypes import c_int
srch = self.dll[6]
srch.restype = c_int
print srch(self.handler,10,10)
def Close(self):
close = self.dll[3]
close.restype = None
self.opened = False
#print close(self.handler)
conn = ADDP(guid)
#print conn.handler
conn.Discover()
#conn.Close()
print conn.handler
i searched a lot for how to handle a handle returned from a c function, but couldn't find much about it, i read the ctypes docs for a while, and then inspected the header file too..
the handle is defined in the header file with
typedef void* addp_handle_t;
so i assumed i had to set 'restype' to 'c_void_p', the function always returns 'None'
its specified in the header file that it returns 'None' when an error has occurred, else it return the handle to ADDP session.
another thing, this dll does not export functions by name... i had to, more or less, guess what function is what by expected bytes in arguments.
any ideas on this?
i've found a project on google code but apparently it didn't go far...
if you need any other details, just say
Related
i am trying to work on easyhook in python and here is my code
# Hook/EasyHook.py
from ctypes import *
from ctypes.util import find_library
from pathlib import Path
c_ulong_p = POINTER(c_ulong)
c_void_pp=POINTER(c_void_p)
res_path = str(Path(__file__).parent / 'res' / 'EasyHook64.dll')
lib_path = find_library(res_path)
clib = cdll.LoadLibrary(lib_path)
class TRACED_HOOK_HANDLE(Structure):
_fields_ = [("Link", c_void_p)]
lh_install_hook = clib.LhInstallHook
lh_install_hook.restype = c_ulong
lh_install_hook.argtypes = [c_void_p, c_void_p, c_void_p, TRACED_HOOK_HANDLE]
# some definition of other functions...
if __name__ == '__main__':
from ctypes.wintypes import *
t_dll = CDLL('User32.dll')
test=lambda:t_dll.MessageBoxW(None, 'hi content!', 'hi title!', 0)
test()
interface=CFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
def fake_function(handle, title, message, flag):
return t_original(handle, "hooked "+title, "hooked "+message, flag)
t_hook_info = TRACED_HOOK_HANDLE(None)
if lh_install_hook(t_dll.MessageBoxW, interface(fake_function), None, byref(t_hook_info)):
raise Exception("Hook error[%s]:\n%s" % (rtl_get_last_error(), rtl_get_last_error_string()))
# error occur here and the program terminate
# some other tests...
after a try, it exit on code 0xC0000005 when running to lh_install_hook calling and without any exception printed
then I tried to use those Api after inject into a C++ program by
lh_install_hook(func_address, interface(hook_function), None, byref(hook_info))
where func_address is the actual address of target call,and it cause
python38.dll+24174
_ctypes.pyd+A48D
python38.dll+33E00
python38.dll+3DA6E
_ctypes.pyd+3C69
_ctypes.pyd+38AB
python38.dll+507F5
python38.dll+491C8
is there any way to make it run?
Edit:
here is my code inject and run in the c++ programe
# Hook/__init__.py
from .EasyHook import *
class Hook(object):
def __init__(self, func_address: int):
self.enabled = False
self.hook_info = TRACED_HOOK_HANDLE(None)
self._ACLEntries = (c_ulong * 1)(0)
self.ACLEntries = cast(self._ACLEntries, POINTER(c_ulong))
interface = CFUNCTYPE(self.restype, *self.argtypes)
def hook_function(*args):
return self.hook_function(*args)
if lh_install_hook(func_address, interface(hook_function), None, byref(self.hook_info)):
raise LocalHookError()
# error occur here and the program terminate
# some other codes...
restype = c_void_p
argtypes = []
def hook_function(self, *args):
return self.original(*args)
# main.py
from Hook import Hook
from ctypes import *
from ctypes.wintypes import *
class kernel32_beep_hook(Hook):
restype = c_bool
argtypes = [DWORD,DWORD]
def hook_function(self, a1, a2):
if logger is not None:
logger.log('beep_hook','%s,%s'%(a1,a2))
return self.original(a1,a2)
# some skip codes
addr=kernel32.GetProcAddress(kernel32_module,b"Beep")
ctypes.windll.kernel32.Beep(500,500)
hook=kernel32_beep_hook(addr)
# error occur here and the program terminate
According to [GitHub]: EasyHook/EasyHook - (master) EasyHook/Public/easyhook.h:
typedef struct _HOOK_TRACE_INFO_
{
PLOCAL_HOOK_INFO Link;
}HOOK_TRACE_INFO, *TRACED_HOOK_HANDLE;
TRACED_HOOK_HANDLE is actually a pointer (although its name suggests the opposite), therefore your lh_install_hook.argtypes (1st snippet) is incorrect. It should be:
lh_install_hook.argtypes = [c_void_p, c_void_p, c_void_p, POINTER(TRACED_HOOK_HANDLE)]
Technically, you ran into [SO]: C function called from Python via ctypes returns incorrect value (#CristiFati's answer).
Regarding no exception being thrown, maybe [SO]: Python exception thrown by libtidy is amusingly impossible to catch (#CristiFati's answer) should shed some light.
This should get past the problem, at least the main one. I'm not sure whether there are others, as I didn't install (or build) the .lib, so I didn't run your code. My knowledge is very limited (so this might be complete nonsense), but one potential spot to generate problems is TRACED_HOOK_HANDLE->Link being initialized to NULL.
Not wx, gtk3, pyqt etc...
I need something like:
cef.Initialize(settings=settings)
window_info = cef.WindowInfo()
browser = cef.CreateBrowserSync(url="localhost:8080/", window_title="Hello World!" icon="myicon.png")
On Linux execute xseticon program programmtically using os.system() function or similar, see: http://www.leonerd.org.uk/code/xseticon/ .
On Windows use ctypes built-in Python module to execute native win32 functions. Example code below. The _hWnd variable holds window handle which can be obtained by calling browser.GetWindowHandle().
from ctypes import *
from ctypes.wintypes import *
from os import path
import platform
LRESULT = c_int64 if platform.architecture()[0] == "64bit" else c_long
SendMessage = windll.user32.SendMessageW
SendMessage.restype = LRESULT
SendMessage.argtypes = [HWND, UINT, WPARAM, LPARAM]
GetModuleHandle = windll.kernel32.GetModuleHandleW
GetModuleHandle.restype = HMODULE
GetModuleHandle.argtypes = [LPCWSTR]
IMAGE_ICON = 1
LR_LOADFROMFILE = 0x00000010
LR_CREATEDIBSECTION = 0x00002000
LoadImage = windll.user32.LoadImageW
LoadImage.restype = HANDLE
LoadImage.argtypes = [HINSTANCE, LPCWSTR, UINT, c_int, c_int, UINT]
RelPath = lambda file : path.join(path.dirname(path.abspath(__file__)), file)
def AlterIcon(_hWnd, lpszIcon):
WM_SETICON = 0x0080
ICON_BIG = 1
hModel = GetModuleHandle(None)
hIcon = LoadImage(hModel,
RelPath(lpszIcon),
IMAGE_ICON,
0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION)
SendMessage(_hWnd, WM_SETICON, ICON_BIG, hIcon)
Ref: http://qaru.site/questions/7837596/how-to-include-image-in-message-box-using-ctypes-in-python
I am working on Python 3 and tried installing pam, which was successful. It installed the pam-0.1.4 version.
C:\Users\anjan>pip install pam
Collecting pam
Downloading https://files.pythonhosted.org/packages/d6/cb/73c7725f4c7ee14205d85c6777a44f9b33e51d31fd5fc1bb0aa7f35cf8d2/pam-0.1.4.tar.gz
Building wheels for collected packages: pam
Running setup.py bdist_wheel for pam ... done
Stored in directory: C:\Users\anjan\AppData\Local\pip\Cache\wheels\24\8d\30\48fca5978b858d699565ee0cde875798229022bbb4e86e8b43
Successfully built pam
Installing collected packages: pam
Successfully installed pam-0.1.4
Next when I try to import pam it throws out the following syntax error. Looks like the pam.py file is still on Python2.x.
Does anyone know how to resolve this issue?
>>> import pam
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\anjan\Anaconda3\lib\site-packages\pam.py", line 129
print authenticate(getpass.getuser(), getpass.getpass())
^
SyntaxError: invalid syntax
The pam.py file looks like as shown below.
# (c) 2007 Chris AtLee <chris#atlee.ca>
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php
"""
PAM module for python
Provides an authenticate function that will allow the caller to authenticate
a user against the Pluggable Authentication Modules (PAM) on the system.
Implemented using ctypes, so no compilation is necessary.
"""
__all__ = ['authenticate']
from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof
from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int
from ctypes.util import find_library
LIBPAM = CDLL(find_library("pam"))
LIBC = CDLL(find_library("c"))
CALLOC = LIBC.calloc
CALLOC.restype = c_void_p
CALLOC.argtypes = [c_uint, c_uint]
STRDUP = LIBC.strdup
STRDUP.argstypes = [c_char_p]
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
# Various constants
PAM_PROMPT_ECHO_OFF = 1
PAM_PROMPT_ECHO_ON = 2
PAM_ERROR_MSG = 3
PAM_TEXT_INFO = 4
class PamHandle(Structure):
"""wrapper class for pam_handle_t"""
_fields_ = [
("handle", c_void_p)
]
def __init__(self):
Structure.__init__(self)
self.handle = 0
class PamMessage(Structure):
"""wrapper class for pam_message structure"""
_fields_ = [
("msg_style", c_int),
("msg", POINTER(c_char)),
]
def __repr__(self):
return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
class PamResponse(Structure):
"""wrapper class for pam_response structure"""
_fields_ = [
("resp", POINTER(c_char)),
("resp_retcode", c_int),
]
def __repr__(self):
return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
CONV_FUNC = CFUNCTYPE(c_int,
c_int, POINTER(POINTER(PamMessage)),
POINTER(POINTER(PamResponse)), c_void_p)
class PamConv(Structure):
"""wrapper class for pam_conv structure"""
_fields_ = [
("conv", CONV_FUNC),
("appdata_ptr", c_void_p)
]
PAM_START = LIBPAM.pam_start
PAM_START.restype = c_int
PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
POINTER(PamHandle)]
PAM_END = LIBPAM.pam_end
PAM_END.restpe = c_int
PAM_END.argtypes = [PamHandle, c_int]
PAM_AUTHENTICATE = LIBPAM.pam_authenticate
PAM_AUTHENTICATE.restype = c_int
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
def authenticate(username, password, service='login'):
"""Returns True if the given username and password authenticate for the
given service. Returns False otherwise
``username``: the username to authenticate
``password``: the password in plain text
``service``: the PAM service to authenticate against.
Defaults to 'login'"""
#CONV_FUNC
def my_conv(n_messages, messages, p_response, app_data):
"""Simple conversation function that responds to any
prompt where the echo is off with the supplied password"""
# Create an array of n_messages response objects
addr = CALLOC(n_messages, sizeof(PamResponse))
p_response[0] = cast(addr, POINTER(PamResponse))
for i in range(n_messages):
if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
pw_copy = STRDUP(str(password))
p_response.contents[i].resp = pw_copy
p_response.contents[i].resp_retcode = 0
return 0
handle = PamHandle()
conv = PamConv(my_conv, 0)
retval = PAM_START(service, username, pointer(conv), pointer(handle))
if retval != 0:
# TODO: This is not an authentication error, something
# has gone wrong starting up PAM
PAM_END(handle, retval)
return False
retval = PAM_AUTHENTICATE(handle, 0)
e = PAM_END(handle, retval)
return retval == 0 and e == 0
if __name__ == "__main__":
import getpass
print authenticate(getpass.getuser(), getpass.getpass())
print is a function in Python 3.
Change it to
print(authenticate(getpass.getuser(), getpass.getpass()))
# ^ Note the braces that make it a function call
The way you have it written is valid only in Python 2.
Pam 0.1.4 is for python2, hence the error. There is a pam available for python3 , but it is called as simplepam -0.1.5 . I guess this should be a probable fix for my question.
[link] (pypi.org/project/simplepam)
I'm developing instrument drivers for testing at work. It's first developed in
C using the IVI/Visa standard. I am then using a python wrapper for a dll of the C to use the functions in the python environment. However with the current one I am getting an error:
Traceback (most recent call last):
File "D:/CAS/Development/LvDrivers/Python/Python_wrapper/ADIag81110a.py", line 1362, in <module>
PGEN.ConfigureTransitionCoupling(1, "1")
File "D:/CAS/Development/LvDrivers/Python/Python_wrapper/ADIag81110a.py", line 1305, in ConfigureTransitionCoupling
ADIag81110aLib().ADIag81110a_ConfigureTransitionCoupling( self.vi, transitionEnabling , channelName )
WindowsError: exception: access violation reading 0x00000031
It happens with any function I use from the C. All of the functions work correctly when they are ran directly in C. He is is the C definition of that function:
ViStatus _VI_FUNC ADIag81110a_ConfigureTransitionCoupling (ViSession vi,
ViInt32 transitionEnabling, ViChar channelName[])
{
ViStatus error = VI_SUCCESS;
checkErr( Ivi_LockSession (vi, VI_NULL));
viCheckParm(Ivi_SetAttributeViInt32 (vi, channelName, ADIAG81110A_ATTR_TRANSITION_COUPLING,
0, transitionEnabling), 2, "Transition Coupling");
Error:
Ivi_UnlockSession (vi, VI_NULL);
return error;
}
The python might seem a little complicated but i'll only be posting the code that I feel is relevant to declaration of the ConfigureTransitionCoupling function that I'm using as an example and the ctypes declarations:
import os
import visa_exceptions
from visa_messages import completion_and_error_messages
from vpp43_constants import *
from vpp43_types import *
from ctypes import cdll
if os.name == 'nt':
from ctypes import windll
else:
from ctypes import CFUNCTYPE as FUNCTYPE
import warnings
class Singleton(object):
def __new__(cls, *args, **kwds):
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = object.__new__(cls)
it.init(*args, **kwds)
return it
class ADIag81110a_lib(Singleton):
def __call__(self, force_cdecl=False):
if self.__lib is None or self.__cdecl_lib is None:
self.load_library()
if force_cdecl:
return self.__cdecl_lib
return self.__lib
def init(self):
self.__lib = self.__cdecl_lib = None
def load_library(self, path=None):
if os.name == 'nt':
path = "C:\Program Files (x86)\IVI Foundation\IVI\Bin\ADIag81110a_32.dll"
self.__lib = windll.LoadLibrary(path)
self.__cdecl_lib = cdll.LoadLibrary(path)
elif os.name == 'posix':
if not path:
path = "please put path to unix/linix here"
self.__lib = self.__cdecl_lib = cdll.LoadLibrary(path)
else:
self.__lib = self.__cdecl_lib = None
raise visa_exceptions.OSNotSupported, os.name
self.__initialize_library_functions()
return None
def __initialize_library_functions(self):
self.__set_argument_types("ADIag81110a_ConfigureTransitionCoupling", [ ViSession , ViInt32 , ViChar ])
def __set_argument_types(self, inst_function, types, force_cdecl=False, may_be_missing=True):
if force_cdecl:
library = self.__cdecl_lib
else:
library = self.__lib
try:
getattr(library, inst_function).argtypes = types
except AttributeError:
if not may_be_missing:
raise
ADIag81110aLib = ADIag81110a_lib()
class ADIag81110a():
def ConfigureTransitionCoupling(self , transitionEnabling , channelName ):
ADIag81110aLib().ADIag81110a_ConfigureTransitionCoupling( self.vi, transitionEnabling , channelName )
if __name__ == '__main__':
#This initilises the communication with the instrument
PGEN = ADIag81110a("GPIB0::10::INSTR")
test = PGEN.ReadOutputImpedance("1")
print test
I have looked at other people posting about their error and I feel they are using C-types in a different way to me so I am unable to apply their solutions.
Any help is appreciated. This is also my first post on stack overflow so feel free to point out any issues with my post :)
I have three pieces of code that i'm working with at the moment:
A closed source application (Main.exe)
A closed source VB COM object implemented as a dll (comobj.dll)
Code that I am developing in Python
comobj.dll hosts a COM object (lets say, 'MainInteract') that I would like to use from Python. I can already use this object perfectly fine from IronPython, but due to other requirements I need to use it from regular Python. I believe the best method here is to use win32com, but I can't quite make any headway at all.
First, some working IronPython code:
import clr
import os
import sys
__dir__ = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, __dir__)
sys.path.append(r"C:\Path\To\comobj.dll") #This is where the com object dll actually is
clr.AddReferenceToFileAndPath(os.path.join(__dir__, r'comobj_1_1.dll')) #This is the .NET interop assembly that was created automatically via SharpDevelop's COM Inspector
from comobj_1_1 import clsMainInteract
o = clsMainInteract()
o.DoStuff(True)
And now the code that I attempted in regular Python:
>>> import win32com.client
>>> win32com.client.Dispatch("{11111111-comobj_guid_i_got_from_com_inspector}")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python26\lib\site-packages\win32com\client\__init__.py", line 95, in Dispatch
dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 104, in _GetGoodDispatchAndUserName
return (_GetGoodDispatch(IDispatch, clsctx), userName)
File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 84, in _GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
pywintypes.com_error: (-2147221164, 'Class not registered', None, None)
I have also attempted using the friendly name of the TLB:
>>> import win32com.client
>>> win32com.client.Dispatch("Friendly TLB Name I Saw")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python26\lib\site-packages\win32com\client\__init__.py", line 95, in Dispatch
dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 104, in _GetGoodDispatchAndUserName
return (_GetGoodDispatch(IDispatch, clsctx), userName)
File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 84, in _GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
pywintypes.com_error: (-2147221005, 'Invalid class string', None, None)
In fact, the only success I've had was this:
import pythoncom
tlb = pythoncom.LoadRegTypeLib("{11111111-comobj_guid_i_got_from_com_inspector}",1,1,0)
>>> tlb
<PyITypeLib at 0x00AD7D78 with obj at 0x0025EDF0>
>>> tlb.GetDocumentation(1)
(u'clsMainInteract', None, 0, None)
But i'm not sure how to go from there to getting an object. I think my problem is that I need to load the dll into my process and get it to register itself with my process's COM source, so I can properly CoCreateInstance / win32com.client.Dispatch() on it.
I have also seen Activation Contexts referenced, especially when talking about 'no registration COM', but typically in a sentences like "Windows will create a context for you if you specify the right stuff in your .manifest files". I'd like to avoid manifest files if possible, as one would be required in the same folder as the (closed source) COM object dll, and i'd rather not drop any files in that directory if I can avoid it.
Thanks for the help.
What I did to access Free Download Manager's type library was the following:
import pythoncom, win32com.client
fdm = pythoncom.LoadTypeLib('fdm.tlb')
downloads_stat = None
for index in xrange(0, fdm.GetTypeInfoCount()):
type_name = fdm.GetDocumentation(index)[0]
if type_name == 'FDMDownloadsStat':
type_iid = fdm.GetTypeInfo(index).GetTypeAttr().iid
downloads_stat = win32com.client.Dispatch(type_iid)
break
downloads_stat.BuildListOfDownloads(True, True)
print downloads_stat.Download(0).Url
The code above will print the URL of the first download.
Here is a method I devised to load a COM object from a DLL. It was based on a lot of reading about COM, etc. I'm not 100% sure about the last lines, specifically d=. I think that only works if IID_Dispatch is passed in (which you can see if the default param).
In addition, I believe this code leaks - for one, the DLL is never unloaded (use ctypes.windll.kernel32.FreeLibraryW) and I believe the COM ref counts for the initial class factory are off by one, and thus never get released. But still, this works for my application.
import pythoncom
import win32com.client
def CreateInstanceFromDll(dll, clsid_class, iid_interface=pythoncom.IID_IDispatch, pUnkOuter=None, dwClsContext=pythoncom.CLSCTX_SERVER):
from uuid import UUID
from ctypes import OleDLL, c_long, byref
e = OleDLL(dll)
clsid_class = UUID(clsid_class).bytes_le
iclassfactory = UUID(str(pythoncom.IID_IClassFactory)).bytes_le
com_classfactory = c_long(0)
hr = e.DllGetClassObject(clsid_class, iclassfactory, byref(com_classfactory))
MyFactory = pythoncom.ObjectFromAddress(com_classfactory.value, pythoncom.IID_IClassFactory)
i = MyFactory.CreateInstance(pUnkOuter, iid_interface)
d = win32com.client.__WrapDispatch(i)
return d
For a useful utility module that wraps the object-from-DLL case, as well as others, see https://gist.github.com/4219140
__all__ = (
####### Class Objects
#CoGetClassObject - Normal, not wrapped
'CoDllGetClassObject', #Get ClassObject from a DLL file
####### ClassFactory::CreateInstance Wrappers
'CoCreateInstanceFromFactory', #Create an object via IClassFactory::CreateInstance
'CoCreateInstanceFromFactoryLicenced', #Create a licenced object via IClassFactory2::CreateInstanceLic
###### Util
'CoReleaseObject', #Calls Release() on a COM object
###### Main Utility Methods
#'CoCreateInstance', #Not wrapped, normal call
'CoCreateInstanceLicenced', #CoCreateInstance, but with a licence key
###### Hacky DLL methods for reg-free COM without Activation Contexts, manifests, etc
'CoCreateInstanceFromDll', #Given a dll, a clsid, and an iid, create an object
'CoCreateInstanceFromDllLicenced', #Given a dll, a clsid, an iid, and a license key, create an object
)
IID_IClassFactory2 = "{B196B28F-BAB4-101A-B69C-00AA00341D07}"
from uuid import UUID
from ctypes import OleDLL, WinDLL, c_ulong, byref, WINFUNCTYPE, POINTER, c_char_p, c_void_p
from ctypes.wintypes import HRESULT
import pythoncom
import win32com.client
import logging
log = logging.getLogger(__name__)
def _raw_guid(guid):
"""Given a string GUID, or a pythoncom IID, return the GUID laid out in memory suitable for passing to ctypes"""
return UUID(str(guid)).bytes_le
proto_icf2_base = WINFUNCTYPE(HRESULT,
c_ulong,
c_ulong,
c_char_p,
c_ulong,
POINTER(c_ulong),
)
IClassFactory2__CreateInstanceLic = proto_icf2_base(7, 'CreateInstanceLic', (
(1, 'pUnkOuter'),
(1 | 4, 'pUnkReserved'),
(1, 'riid'),
(1, 'bstrKey'),
(2, 'ppvObj'),
), _raw_guid(IID_IClassFactory2))
#--------------------------------
#--------------------------------
def _pc_wrap(iptr, resultCLSID=None):
#return win32com.client.__WrapDispatch(iptr)
log.debug("_pc_wrap: %s, %s"%(iptr, resultCLSID))
disp = win32com.client.Dispatch(iptr, resultCLSID=resultCLSID)
log.debug("_pc_wrap: %s (%s)", disp.__class__.__name__, disp)
return disp
def CoCreateInstanceFromFactory(factory_ptr, iid_interface=pythoncom.IID_IDispatch, pUnkOuter=None):
"""Given a factory_ptr whose interface is IClassFactory, create the instance of clsid_class with the specified interface"""
ClassFactory = pythoncom.ObjectFromAddress(factory_ptr.value, pythoncom.IID_IClassFactory)
i = ClassFactory.CreateInstance(pUnkOuter, iid_interface)
return i
def CoCreateInstanceFromFactoryLicenced(factory_ptr, key, iid_interface=pythoncom.IID_IDispatch, pUnkOuter=None):
"""Given a factory_ptr whose interface is IClassFactory2, create the instance of clsid_class with the specified interface"""
requested_iid = _raw_guid(iid_interface)
ole_aut = WinDLL("OleAut32.dll")
key_bstr = ole_aut.SysAllocString(unicode(key))
try:
obj = IClassFactory2__CreateInstanceLic(factory_ptr, pUnkOuter or 0, c_char_p(requested_iid), key_bstr)
disp_obj = pythoncom.ObjectFromAddress(obj, iid_interface)
return disp_obj
finally:
if key_bstr:
ole_aut.SysFreeString(key_bstr)
#----------------------------------
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', (), pythoncom.IID_IUnknown)
IUnknown__Release(obj_ptr)
#-----------------------------------
def CoCreateInstanceLicenced(clsid_class, key, pythoncom_iid_interface=pythoncom.IID_IDispatch, dwClsContext=pythoncom.CLSCTX_SERVER, pythoncom_wrapdisp=True, wrapas=None):
"""Uses IClassFactory2::CreateInstanceLic to create a COM object given a licence key."""
IID_IClassFactory2 = "{B196B28F-BAB4-101A-B69C-00AA00341D07}"
ole = OleDLL("Ole32.dll")
clsid_class_raw = _raw_guid(clsid_class)
iclassfactory2 = _raw_guid(IID_IClassFactory2)
com_classfactory = c_void_p(0)
ole.CoGetClassObject(clsid_class_raw, dwClsContext, None, iclassfactory2, byref(com_classfactory))
try:
iptr = CoCreateInstanceFromFactoryLicenced(
factory_ptr = com_classfactory,
key=key,
iid_interface=pythoncom_iid_interface,
pUnkOuter=None,
)
if pythoncom_wrapdisp:
return _pc_wrap(iptr, resultCLSID=wrapas or clsid_class)
return iptr
finally:
if com_classfactory:
CoReleaseObject(com_classfactory)
#-----------------------------------------------------------
#DLLs
def CoDllGetClassObject(dll_filename, clsid_class, iid_factory=pythoncom.IID_IClassFactory):
"""Given a DLL filename and a desired class, return the factory for that class (as a c_void_p)"""
dll = OleDLL(dll_filename)
clsid_class = _raw_guid(clsid_class)
iclassfactory = _raw_guid(iid_factory)
com_classfactory = c_void_p(0)
dll.DllGetClassObject(clsid_class, iclassfactory, byref(com_classfactory))
return com_classfactory
def CoCreateInstanceFromDll(dll, clsid_class, iid_interface=pythoncom.IID_IDispatch, pythoncom_wrapdisp=True, wrapas=None):
iclassfactory_ptr = CoDllGetClassObject(dll, clsid_class)
try:
iptr = CoCreateInstanceFromFactory(iclassfactory_ptr, iid_interface)
if pythoncom_wrapdisp:
return _pc_wrap(iptr, resultCLSID=wrapas or clsid_class)
return iptr
finally:
CoReleaseObject(iclassfactory_ptr)
def CoCreateInstanceFromDllLicenced(dll, clsid_class, key, iid_interface=pythoncom.IID_IDispatch, pythoncom_wrapdisp=True, wrapas=None):
iclassfactory2_ptr = CoDllGetClassObject(dll, clsid_class, iid_factory=IID_IClassFactory2)
try:
iptr = CoCreateInstanceFromFactoryLicenced(iclassfactory2_ptr, key, iid_interface)
if pythoncom_wrapdisp:
return _pc_wrap(iptr, resultCLSID=wrapas or clsid_class)
return iptr
finally:
CoReleaseObject(iclassfactory2_ptr)