I need a global system hook which will intercept GDI and get all text on screen from the application. I'm doing this following the instructions here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644960(v=vs.85).aspx#installing_releasing
Code so far:
import ctypes
import atexit
import time
import sys
from ctypes import wintypes, CFUNCTYPE, POINTER, c_int, c_uint, c_void_p, windll, pointer, byref, WinError
import win32con
WH_GETMESSAGE = 3
WH_CALLWNDPROC = 4
cmp_func = CFUNCTYPE(c_int, c_int, wintypes.HINSTANCE, POINTER(c_void_p))
gdi_dll = getattr(ctypes.cdll, 'Gdi32')
SetWindowsHookExA = ctypes.windll.user32.SetWindowsHookExA
SetWindowsHookExA.restype = wintypes.HHOOK
SetWindowsHookExA.argtypes = [c_int, cmp_func, wintypes.HINSTANCE, wintypes.DWORD]
UnhookWindowsHookEx = ctypes.windll.user32.UnhookWindowsHookEx
def _callback_pointer(handler):
"""Create and return C-pointer"""
print('handler=',cmp_func(handler))
return cmp_func(handler)
def callback(*args, **kwargs):
print(args, kwargs)
res = windll.user32.CallNextHookEx(*args, **kwargs)
print(res)
return res
hinstDLL = ctypes.windll.LoadLibrary('Gdi32')._handle
print('hinstDLL=', hinstDLL)
ctypes.windll.kernel32.GetProcAddress.restype = c_int
ctypes.windll.kernel32.GetProcAddress.argtypes = [c_int, ctypes.c_char_p]
hkprcSysMsg = ctypes.windll.kernel32.GetProcAddress(hinstDLL, "TextOutA".encode(encoding='ascii'))
if not hkprcSysMsg:
error = ctypes.windll.kernel32.GetLastError()
print(WinError(error))
print('hkprcSysMsg=', hkprcSysMsg)
hook = ctypes.windll.user32.SetWindowsHookExA(
WH_CALLWNDPROC,
_callback_pointer(callback),
hkprcSysMsg,
# hinstDLL,
0
)
print('hook=',hook)
error = ctypes.windll.kernel32.GetLastError()
print(WinError(error))
if not hook:
sys.exit()
while True:
_process_win_msgs() #not included in this question
time.sleep(0.02)
atexit.register(UnhookWindowsHookEx, hook)
This consistently gets me
Error 126: Module not found.
But if I replace hkprcSysMsg with hinstDLL, the hook establishes and seems to work, although all it does it crashes all 32-bit programs I touch while it's running, which is sort of expectable, but not the behavior I need.
So, why does it let me intercept the whole library, but not the particular procedure?
What should I do to get it right and read all the text system-wide before it appears on the screen?