Related
This program works on Unix and I'm trying to transition it to windows.
It uses multiprocessing and I understand it's an issue with being forced to use spawning on windows opposed to forking on linux for multiprocessing.
It's to do with the subprocess loading the models that I load on the main thread.
When Consumer() is called, during the init() it calls a function from another file that loads some tensorflow models.
Consumer()
import os
import time
import sys
from PIL import Image
import subprocess
from multiprocessing import Process, Queue
import t2identify
class Consumer:
def __init__(self, frameSource, layer):
self.directory = os.environ["directory"]
self.source = frameSource
self.task = layer
if layer == "screen":
self.layer = t2identify.identifyImage("apps")
elif layer == "detail":
self.layer = "detail"
self.imagesChecked = 0
self.errorsFound = 0
self.previousApp = "none"
self.appDetail = {
"mobile": t2identify.identifyImage("mobile"),
}
def start(self, state, queue):
self.state = state
consumer1 = Process(target=self.consumeImage, args=(queue,))
consumer1.start()
consumer2 = Process(target=self.consumeImage, args=(queue,))
consumer2.start()
consumer1.join()
consumer2.join()
t2identify.identifyImage() involves loading models.
t2identify.py
import matplotlib.pyplot as plt
import numpy as np
import os
from PIL import Image
class identifyImage():
def __init__(self, layer):
import tensorflow as tf
availableLayers = {
"apps":"C:/Users/PycharmProjects/NNs/tf/appModel",
}
self.selectedLayer = availableLayers[layer]
self.model = tf.saved_model.load(self.selectedLayer)
self.label = self.loadLabels(availableLayers[layer]+"/labels.txt")
self.img_height = 224
self.img_width = 224
...
I'm confident the issue is when the consumer subprocess starts, the models that are loaded here are loaded again, why it says they're not found I'm not sure.
main.py
import pathos
import multiprocessing
import os
import time
import shutil
from os import path
from producer import Producer
from consumer import Consumer
from controller import Controller
from tqdm import tqdm
import re
import sys
from env import environmentVariables as Environment
class Main:
def start(self, test):
self.createDirectories()
screenQueue = multiprocessing.Queue()
detailQueue = multiprocessing.Queue()
self.producer = Producer(["SavedFrames", "ScreenFrames", "DetailFrames", "VisualFrames"])
self.producerProcess = multiprocessing.Process(target=self.producer.start,
args=(self.producerEvent, self.producerFrameRate, self.state,
self.expected, self.iteration, self.testCaseNumber,
[screenQueue, detailQueue]))
self.screenConsumer = Consumer("ScreenFrames", "screen")
# MODELS ARE LOADED
self.detailConsumer = Consumer("DetailFrames", "detail")
self.screenConsumerProcess = multiprocessing.Process(target=self.screenConsumer.start,
args=(self.state, screenQueue))
self.detailConsumerProcess = multiprocessing.Process(target=self.detailConsumer.start,
args=(self.state, detailQueue))
try:
# Set the new thread to run the controller which performs the test cases
self.controllerStart = Controller(self.producerEvent, self.producerFrameRate, self.state, self.expected, self.iteration,
self.testCaseNumber, self.progress)
self.controllerProcess = multiprocessing.Process(target=self.controllerStart.start, args=(test,))
except:
print("ERROR")
return False
self.producerProcess.start()
# FAILS on starting screenConsumerProcess (see error)
self.screenConsumerProcess.start()
self.detailConsumerProcess.start()
self.controllerProcess.start()
self.producerProcess.join()
self.screenConsumerProcess.join()
self.detailConsumerProcess.join()
self.controllerProcess.join()
self.zipFiles()
self.sendLogs()
return True
...
if __name__ == "__main__":
testing = Main()
results = testing.start()
The error:
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "AppData\Local\Programs\Python\Python37\lib\multiprocessing\spawn.py", line 105, in spawn_main
exitcode = _main(fd)
File "\AppData\Local\Programs\Python\Python37\lib\multiprocessing\spawn.py", line 115, in _main
self = reduction.pickle.load(from_parent)
File "\venv\lib\site-packages\keras\saving\pickle_utils.py", line 48, in deserialize_model_from_bytecode
model = save_module.load_model(temp_dir)
File "\venv\lib\site-packages\keras\utils\traceback_utils.py", line 67, in error_handler
Traceback (most recent call last):
File "C:/Users/PycharmProjects/project/src/main.py", line 353, in <module>
raise e.with_traceback(filtered_tb) from None
File "\venv\lib\site-packages\tensorflow\python\saved_model\load.py", line 978, in load_internal
results = testing.start(data)
File "/PycharmProjects/project/src/main.py", line 95, in start
self.screenConsumerProcess.start()
File "\AppData\Local\Programs\Python\Python37\lib\multiprocessing\process.py", line 112, in start
str(err) + "\n You may be trying to load on a different device "
FileNotFoundError: Unsuccessful TensorSliceReader constructor: Failed to find any matching files for ram://4e4e1c18-ece9-4d99-88c3-5c2ee965c92a/variables/variables
You may be trying to load on a different device from the computational device. Consider setting the `experimental_io_device` option in `tf.saved_model.LoadOptions` to the io_device such as '/job:localhost'.
The models that are loaded aren't accessable by subprocesses. As ram://4e4e1c18-ece9-4d99-88c3-5c2ee965c92a/variables/variables is temp_dir in keras/pickle_utils.py model = save_module.load_model(temp_dir). Which is where the error occured.
Am I running out of ram? Or do I need to change some multiprocessing code now that I'm on windows.
EDIT:
I suspect it's most likely to do with windows reimporting everything once a new process starts (spawning). While doing this, the models are reloaded, and that's where the error occurs. I am still however unsure how to go about resolving this, apart from loading the models in main then passing the models into the subprocesses as parameters... which seems like a subpar solution.
EDIT2:
Now looking into using pathos which uses dill and not pickle. As I suspect the issue is with when I start the consumer process, the target is a class, which is not pickleable.
To avoid pickling of the tensorflow models, try moving the creation of these models to the process that uses them:
class Consumer:
def __init__(self, frameSource, layer):
self.directory = os.environ["directory"]
self.source = frameSource
self.task = layer
self._layer = layer
# The following code is moved to the consumeImage method:
"""
if layer == "screen":
self.layer = t2identify.identifyImage("apps")
elif layer == "detail":
self.layer = "detail"
"""
self.imagesChecked = 0
self.errorsFound = 0
self.previousApp = "none"
# The following code is moved to consumeImage:
"""
self.appDetail = {
"mobile": t2identify.identifyImage("mobile"),
}
"""
def start(self, state, queue):
self.state = state
consumer1 = Process(target=self.consumeImage, args=(queue,))
consumer1.start()
consumer2 = Process(target=self.consumeImage, args=(queue,))
consumer2.start()
consumer1.join()
consumer2.join()
def consumeImage(self, queue):
self.appDetail = {
"mobile": t2identify.identifyImage("mobile"),
}
if self._layer == "screen":
self.layer = t2identify.identifyImage("apps")
elif self._layer == "detail":
self.layer = "detail"
...
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.
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'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
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)