How do I fill in the pData parameter of the GetRawInputData module - python
I'm semi-new to ctypes and I'm having trouble with how to use the GetRawInputData function. I'm not sure how to fill in the 3rd argument. Here's how the code looks right now
def get_raw_input(handle):
dw_size = c_uint()
GetRawInputData(handle, RID_INPUT, None, byref(dw_size), sizeof(RawInputHeader))
lpb = LPBYTE(dw_size)
print(lpb.contents)
GetRawInputData(handle, RID_INPUT, lpb, byref(dw_size), sizeof(RawInputHeader))
print(lpb.contents)
return cast(lpb, POINTER(RawInput))
I've defined the module like so:
GetRawInputData = user32.GetRawInputData
GetRawInputData.argtypes = INT, UINT, LPVOID, PUINT, UINT
GetRawInputData.restype = UINT
GetRawInputData.errcheck = errcheck
When get_raw_input is run, the output is one of either of these:
c_byte(48)
c_byte(0)
c_byte(40)
c_byte(1)
There's some example C++ code here, but I'm not sure how that should look in Python.
Here's the full running code (sorry if it's a bit messy):
window.py (file that runs)
from ctypes import *
from ctypes.wintypes import *
from structures import *
from constants import *
from devices import get_raw_input, register_devices
import random
import sys
def errcheck(result,func,args):
if result is None or result == 0:
raise WinError(get_last_error())
return result
user32 = WinDLL('user32', use_last_error=True)
k32 = WinDLL('kernel32', use_last_error=True)
gdi32 = WinDLL('gdi32',use_last_error=True)
RegisterClass = user32.RegisterClassW
RegisterClass.argtypes = POINTER(WndClass),
RegisterClass.restype = ATOM
RegisterClass.errcheck = errcheck
CreateWindowEx = user32.CreateWindowExW
CreateWindowEx.argtypes = DWORD, LPCWSTR, LPCWSTR, DWORD, INT, INT, INT, INT, HWND, HMENU, HINSTANCE, LPVOID
CreateWindowEx.restype = HWND
CreateWindowEx.errcheck = errcheck
GetModuleHandle = k32.GetModuleHandleW
GetModuleHandle.argtypes = LPCWSTR,
GetModuleHandle.restype = HMODULE
GetModuleHandle.errcheck = errcheck
DefWindowProc = user32.DefWindowProcW
DefWindowProc.argtypes = HWND, UINT, WPARAM, LPARAM
DefWindowProc.restype = LRESULT
ShowWindow = user32.ShowWindow
ShowWindow.argtypes = HWND, INT
ShowWindow.restype = BOOL
UpdateWindow = user32.UpdateWindow
UpdateWindow.argtypes = HWND,
UpdateWindow.restype = BOOL
UpdateWindow.errcheck = errcheck
BeginPaint = user32.BeginPaint
BeginPaint.argtypes = HWND, POINTER(PaintStruct)
BeginPaint.restype = HDC
BeginPaint.errcheck = errcheck
FillRect = user32.FillRect
FillRect.argtypes = HDC, POINTER(RECT), HBRUSH
FillRect.restype = INT
FillRect.errcheck = errcheck
EndPaint = user32.EndPaint
EndPaint.argtypes = HWND, POINTER(PaintStruct)
EndPaint.restype = BOOL
EndPaint.errcheck = errcheck
PostQuitMessage = user32.PostQuitMessage
PostQuitMessage.argtypes = INT,
PostQuitMessage.restype = None
TranslateMessage = user32.TranslateMessage
TranslateMessage.argtypes = POINTER(MSG),
TranslateMessage.restype = BOOL
DispatchMessage = user32.DispatchMessageW
DispatchMessage.argtypes = POINTER(MSG),
DispatchMessage.restype = LRESULT
GetClientRect = user32.GetClientRect
GetClientRect.argtypes = HWND, POINTER(RECT)
GetClientRect.restype = BOOL
GetClientRect.errcheck = errcheck
GetMessage = user32.GetMessageW
GetMessage.argtypes = POINTER(MSG), HWND, UINT, UINT
GetMessage.restype = BOOL
DrawText = user32.DrawTextW
DrawText.argtypes = HDC, LPCWSTR, INT, POINTER(RECT), UINT
DrawText.restype = LRESULT
LoadIcon = user32.LoadIconW
LoadIcon.argtypes = HINSTANCE, LPCWSTR
LoadIcon.restype = HICON
LoadIcon.errcheck = errcheck
LoadCursor = user32.LoadCursorW
LoadCursor.argtypes = HINSTANCE, LPCWSTR
LoadCursor.restype = HCURSOR
LoadCursor.errcheck = errcheck
GetStockObject = gdi32.GetStockObject
GetStockObject.argtypes = INT,
GetStockObject.restype = HGDIOBJ
CreateSolidBrush = gdi32.CreateSolidBrush
CreateSolidBrush.argtypes = COLORREF,
CreateSolidBrush.restype = HBRUSH
TextOut = gdi32.TextOutW
TextOut.argtypes = HDC, INT, INT, LPCWSTR, INT
TextOut.restype = INT
SetBkMode = gdi32.SetBkMode
SetBkMode.argtypes = HDC, INT
SetBkMode.restype = INT
SetTextColor = gdi32.SetTextColor
SetTextColor.argtypes = HDC, COLORREF
SetTextColor.restype = COLORREF
def window_callback(handle, message, w_param, l_param):
ps = PaintStruct()
rect = RECT()
if message == WM_PAINT:
hdc = BeginPaint(handle, byref(ps))
GetClientRect(handle, byref(rect))
SetBkMode(hdc, -1)
SetTextColor(hdc, 0xFFFFFF)
FillRect(hdc, byref(ps.rcPaint), CreateSolidBrush(0x0))
DrawText(hdc, 'Testing oawguna', -1, byref(rect), DT_SINGLELINE|DT_CENTER|DT_VCENTER)
EndPaint(handle, byref(ps))
return 0
elif message == WM_INPUT:
raw_input = get_raw_input(l_param)
elif message == WM_DESTROY:
PostQuitMessage(0)
return 0
return DefWindowProc(handle, message, w_param, l_param)
def run():
instance = GetModuleHandle(None)
class_name = 'TestWindow'
wnd = WndClass()
wnd.style = CS_HREDRAW | CS_VREDRAW
wnd.lpfnWndProc = WNDPROC(window_callback)
wnd.hInstace = instance
wnd.hCursor = LoadCursor(None, IDC_ARROW)
wnd.lpszClassName = class_name
RegisterClass(byref(wnd))
handle = CreateWindowEx(
0, class_name, 'Python Window', WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
None, None, instance, None
)
ShowWindow(handle, SW_NORMAL)
UpdateWindow(handle)
register_devices(handle)
msg = MSG()
while GetMessage(byref(msg), None, 0, 0) != 0:
TranslateMessage(byref(msg))
DispatchMessage(byref(msg))
return msg.wParam
if __name__ == "__main__":
sys.exit(run())
devices.py:
from ctypes import *
from ctypes.wintypes import *
from structures import *
from constants import *
def errcheck(result,func,args):
if result is None or result == -1:
raise WinError(get_last_error())
return result
user32 = WinDLL('user32', use_last_error=True)
RegisterRawInputDevices = user32.RegisterRawInputDevices
RegisterRawInputDevices.argtypes = (RawInputDevice * 7), UINT, UINT
RegisterRawInputDevices.restype = BOOL
RegisterRawInputDevices.errcheck = errcheck
GetRawInputData = user32.GetRawInputData
GetRawInputData.argtypes = INT, UINT, LPVOID, PUINT, UINT
GetRawInputData.restype = UINT
GetRawInputData.errcheck = errcheck
def register_devices(hwnd_target=None):
page = 0x01
devices = (RawInputDevice * 7)(
RawInputDevice(page, 0x01, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x02, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x04, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x05, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x06, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x07, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x08, DW_FLAGS, hwnd_target),
)
RegisterRawInputDevices(devices, len(devices), sizeof(devices[0]))
def get_raw_input(handle):
dw_size = c_uint()
GetRawInputData(handle, RID_INPUT, None, byref(dw_size), sizeof(RawInputHeader))
lpb = LPBYTE(dw_size)
print(lpb.contents)
GetRawInputData(handle, RID_INPUT, lpb, byref(dw_size), sizeof(RawInputHeader))
print(lpb.contents)
return cast(lpb, POINTER(RawInput))
constants.py:
from ctypes.wintypes import *
from ctypes import c_int64, WINFUNCTYPE, c_void_p
LRESULT = c_int64
HCURSOR = c_void_p
USAGE_PAGE = 0x01
USAGE = 0x06
DW_FLAGS = 0
DW_TYPE = 2
WM_INPUT = 0x00FF
WM_PAINT = 0x000F
WM_DESTROY = 0x0002
WM_QUI = 0x0012
RIDI_DEVICENAME = 0x20000007
RIDI_DEVICEINFO = 0x2000000b
ERROR_INSUFFICIENT_BUFFER = 0x7A
WS_EX_CLIENTEDGE = 0x00000200
WS_BORDER = 0x00800000
CS_BYTEALIGNCLIENT = 0x1000
CS_HREDRAW = 2
CS_VREDRAW = 1
SW_NORMAL = 1
CW_USEDEFAULT = -2147483648
WS_OVERLAPPED = 0x00000000
WS_CAPTION = 0x00C00000
WS_SYSMENU = 0x00080000
WS_THICKFRAME = 0x00040000
WS_MINIMIZEBOX = 0x00020000
WS_MAXIMIZEBOX = 0x00010000
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
RID_INPUT = 0x10000003
PM_REMOVE = 0x0001
DT_SINGLELINE = 32
DT_CENTER = 1
DT_VCENTER = 4
IDI_APPLICATION = LPCWSTR(32512)
IDC_ARROW = LPCWSTR(32512)
WHITE_BRUSH = 0
WNDPROC = WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM)
structures.py:
from ctypes import Structure, windll, WINFUNCTYPE, c_int64, Union, c_byte, c_void_p
from ctypes.wintypes import *
from constants import *
HCURSOR = c_void_p
class RawInputDevice(Structure):
_fields_ = [
("usUsagePage", USHORT),
("usUsage", USHORT),
("dwFlags", DWORD),
("hwndTarget", HWND),
]
class RawInputDeviceList(Structure):
_fields_ = [
("hDevice", HANDLE),
("dwType", DWORD),
]
class RIDDeviceInfoHID(Structure):
_fields_ = [
("dwVendorId", DWORD),
("dwProductId", DWORD),
("dwVersionNumber", DWORD),
("usUsagePage", USHORT),
("usUsage", USHORT),
]
class RIDDeviceInfo(Structure):
_fields_ = [
("cbSize", DWORD),
("dwType", DWORD),
("hid", RIDDeviceInfoHID),
]
class WndClass(Structure):
_fields_ = [
("style", UINT),
("lpfnWndProc", WNDPROC),
("cbClsExtra", INT),
("cbWndExtra", INT),
("hInstance", HINSTANCE),
("hIcon", HICON),
("hCursor", HCURSOR),
("hbrBackground", HBRUSH),
("lpszMenuName", LPCWSTR),
("lpszClassName", LPCWSTR),
]
class PaintStruct(Structure):
_fields_ = [
("hdc", HDC),
("fErase", BOOL),
("rcPaint", RECT),
("fRestore", BOOL),
("fIncUpdate", BOOL),
("rgbReserved", BYTE * 32),
]
class RawInputHeader(Structure):
_fields_ = [
("dwType", DWORD),
("dwSize", DWORD),
("hDevice", HANDLE),
("wParam", WPARAM),
]
class RawHID(Structure):
_fields_ = [
("dwSizeHid", DWORD),
("dwCount", DWORD),
("bRawData", BYTE),
]
class MouseStruct(Structure):
_fields_ = [
("usButtonFlags", USHORT),
("usButtonData", USHORT)
]
class MouseUnion(Union):
_fields_ = [
("ulButtons", ULONG),
("data", MouseStruct)
]
class RawMouse(Structure):
_fields_ = [
("usFlags", USHORT),
("data", MouseUnion),
("ulRawButtons", ULONG),
("lLastX", LONG),
("lLastY", LONG),
("ulExtraInformation", ULONG),
]
class RawKeyboard(Structure):
_fields_ = [
("MakeCode", USHORT),
("Flags", USHORT),
("Reserved", USHORT),
("VKey", USHORT),
("Message", UINT),
("ExtraInformation", ULONG),
]
class RawUnion(Union):
_fields_ = [
("mouse", RawMouse),
("keyboard", RawKeyboard),
("hid", RawHID)
]
class RawInput(Structure):
_fields_ = [
("header", RawInputHeader),
("data", RawUnion),
]
Listing [Python.Docs]: ctypes - A foreign function library for Python.
This (a fix) was already covered as part of [SO]: Having trouble using winapi to read input from a device (#CristiFati's answer).
The problem is LPBYTE (POINTER(c_byte)) initialization:
Without parameters, wraps a NULL pointer
With parameter (an integer value) it will store that value in the pointed area (and not use it as size). More, that area will be 064bit large (discovered experimentally) meaning that trying to index outside it triggers Undefined Behavior, even if the program doesn't crash (that is because the area right next isn't claimed by other variables)
To allocate a memory area of a certain size, use create_string_buffer (or an array - but you'll have to cast when passing it to functions expecting pointers).
>>> import ctypes as ct
>>> import ctypes.wintypes as wt
>>>
>>>
>>> pb0 = wt.LPBYTE()
>>> bool(pb0)
False
>>> pb0.contents
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: NULL pointer access
>>>
>>> ll = ct.c_longlong(0x010203040506070809) # Will be truncated (head part - due to little endianness)
>>> pb1 = wt.LPBYTE(ll)
>>> bool(pb1)
True
>>> pb1.contents
c_byte(9)
>>> [pb1[e] for e in range(0, 10)] # #TODO - cfati: will genarate UB !!!
[9, 8, 7, 6, 5, 4, 3, 2, 0, 0]
>>>
>>> buf = ct.create_string_buffer(12)
>>> len(buf)
12
>>> buf[0]
b'\x00'
>>> buf[12]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: invalid index
Related
I want to enumerate devices via Ctypes,conversion problem
I want to use Ctypes to enumerate the device, the following examples are C++ language I am not very good at converting, the following code is half of what I wrote My question is how to conversion this //Enumerate through all devices in Set DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i, &DeviceInfoData);i++) https://social.msdn.microsoft.com/forums/windowsdesktop/en-US/43ab4902-75b9-4c5c-9b54-4567c825539f/how-to-enumerate-hardware-devices-by-using-setupdi?forum=vcgeneral import ctypes as ct from ctypes import wintypes as w SetupAPI = ct.WinDLL('SetupAPI') SetupAPI.SetupDiGetClassDevsW.argtypes = w.LPVOID,w.PWCHAR,w.HWND,w.DWORD SetupAPI.SetupDiGetClassDevsW.restype = w.HANDLE SetupAPI.SetupDiEnumDeviceInfo.argtypes = w.HANDLE,w.DWORD,w.HANDLE SetupAPI.SetupDiEnumDeviceInfo.restype = w.BOOLEAN DIGCF_DEFAULT = 0x00000001 DIGCF_PRESENT = 0x00000002 DIGCF_ALLCLASSES = 0x00000004 DIGCF_PROFILE = 0x00000008 DIGCF_DEVICEINTERFACE = 0x00000010 ClassGuid = None Enumerator = None hwndParent = None Flags = DIGCF_PRESENT | DIGCF_ALLCLASSES DeviceInfoSet = None MemberIndex = None DeviceInfoData = None class SP_DEVINFO_DATA(ct.Structure): _fields_ = [ ('cbSize',w.DWORD), ('ClassGuid',w.LPVOID), ('DevInst',w.DWORD), ('Reserved',w.ULONG), ] SP_DEVINFO_DATA = SP_DEVINFO_DATA() hDevInfo = SetupAPI.SetupDiGetClassDevsW( ClassGuid, Enumerator, hwndParent, Flags ) SP_DEVINFO_DATA.cbSize = ct.sizeof(SP_DEVINFO_DATA) DeviceInfoSet = hex(hDevInfo) MemberIndex = 1 DeviceInfoData = ct.byref(SP_DEVINFO_DATA) SetupDiEnumDeviceInfo = SetupAPI.SetupDiEnumDeviceInfo( DeviceInfoSet, MemberIndex, DeviceInfoData ) print(SetupDiEnumDeviceInfo)
Here's a complete, typesafe example to enumerate a particular device type, in this case USB host controllers: import ctypes as ct from ctypes import wintypes as w import uuid SetupAPI = ct.WinDLL('SetupAPI') # ULONG_PTR is defined as an unsigned integer of the same size as a pointer on the OS, # which is ct.ulonglong on 64-bit or ct.ulong on 32-bit. w.WPARAM happens to follow # that same definition. ULONG_PTR = w.WPARAM # For type safety. A ctypes function will only accept HDEVINFO not any old HANDLE. class HDEVINFO(w.HANDLE): pass # A class to initialize and print GUIDs class GUID(ct.Structure): _fields_ = (('Data1', ct.c_ulong), ('Data2', ct.c_ushort), ('Data3', ct.c_ushort), ('Data4', ct.c_ubyte * 8)) def __repr__(self): return f"GUID('{self}')" def __str__(self): return (f'{{{self.Data1:08x}-{self.Data2:04x}-{self.Data3:04x}-' f'{bytes(self.Data4[:2]).hex()}-{bytes(self.Data4[2:]).hex()}}}') def __init__(self,guid=None): if guid is not None: data = uuid.UUID(guid) self.Data1 = data.time_low self.Data2 = data.time_mid self.Data3 = data.time_hi_version self.Data4[0] = data.clock_seq_hi_variant self.Data4[1] = data.clock_seq_low self.Data4[2:] = data.node.to_bytes(6,'big') PGUID = ct.POINTER(GUID) # See https://learn.microsoft.com/en-us/windows-hardware/drivers/install/guid-devinterface-usb-host-controller GUID_DEVINTERFACE_USB_HOST_CONTROLLER = GUID('{3ABF6F2D-71C4-462A-8A92-1E6861E6AF27}') class SP_DEVINFO_DATA(ct.Structure): _fields_ = (('cbSize', w.DWORD), ('ClassGuid', GUID), ('DevInst', w.DWORD), ('Reserved', ULONG_PTR)) def __repr__(self): return f'SP_DEVINFO_DATA(ClassGuid={self.ClassGuid}, DevInst={self.DevInst})' # Per docs, the cbSize member must be set to the size of this structure. def __init__(self): self.cbSize = ct.sizeof(SP_DEVINFO_DATA) PSP_DEVINFO_DATA = ct.POINTER(SP_DEVINFO_DATA) SetupAPI.SetupDiGetClassDevsW.argtypes = PGUID, w.PWCHAR, w.HWND, w.DWORD SetupAPI.SetupDiGetClassDevsW.restype = HDEVINFO SetupAPI.SetupDiEnumDeviceInfo.argtypes = HDEVINFO, w.DWORD, PSP_DEVINFO_DATA SetupAPI.SetupDiEnumDeviceInfo.restype = w.BOOL SetupAPI.SetupDiDestroyDeviceInfoList.argtypes = HDEVINFO, SetupAPI.SetupDiDestroyDeviceInfoList.restype = w.BOOL DIGCF_DEFAULT = 0x00000001 DIGCF_PRESENT = 0x00000002 DIGCF_ALLCLASSES = 0x00000004 DIGCF_PROFILE = 0x00000008 DIGCF_DEVICEINTERFACE = 0x00000010 ClassGuid = GUID_DEVINTERFACE_USB_HOST_CONTROLLER Enumerator = None hwndParent = None Flags = DIGCF_DEVICEINTERFACE | DIGCF_PRESENT devinfo = SP_DEVINFO_DATA() # Query for the device interface, # then enumerate them one by one by incrementing a zero-based index hDevInfo = SetupAPI.SetupDiGetClassDevsW(ClassGuid, Enumerator, hwndParent, Flags) try: MemberIndex = 0 while SetupAPI.SetupDiEnumDeviceInfo(hDevInfo, MemberIndex, ct.byref(devinfo)): print(devinfo) MemberIndex += 1 finally: SetupAPI.SetupDiDestroyDeviceInfoList(hDevInfo) Output: SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=144) SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=137) SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=17) SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=78) SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=24) SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=98) SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=123) SP_DEVINFO_DATA(ClassGuid={36fc9e60-c465-11cf-8056-444553540000}, DevInst=41) My system has eight USB host controllers and the class GUID matches:
Why does window title get truncated? [Windows API] [duplicate]
I been trying to create Win32 Application by using python (2.7) and ctypes module. Window is created and shown but title of window gets truncated. I got 'M' instead of 'My test window'. What I am doing wrong? Thanks in advance P.S. Here follows the code and screenshot: # -*- coding: utf-8 -*- from sys import platform, exit from ctypes import * from ctypes.wintypes import DWORD, HWND, HANDLE, LPCWSTR, WPARAM, LPARAM, RECT, POINT, MSG WNDPROCTYPE = WINFUNCTYPE(c_int, HWND, c_uint, WPARAM, LPARAM) WS_EX_APPWINDOW = 0x40000 WS_OVERLAPPEDWINDOW = 0xcf0000 WS_CAPTION = 0xc00000 SW_SHOWNORMAL = 1 SW_SHOW = 5 CS_HREDRAW = 2 CS_VREDRAW = 1 CW_USEDEFAULT = 0x80000000 WM_DESTROY = 2 WHITE_BRUSH = 0 class WNDCLASSEX(Structure): _fields_ = [("cbSize", c_uint), ("style", c_uint), ("lpfnWndProc", WNDPROCTYPE), ("cbClsExtra", c_int), ("cbWndExtra", c_int), ("hInstance", HANDLE), ("hIcon", HANDLE), ("hCursor", HANDLE), ("hBrush", HANDLE), ("lpszMenuName", LPCWSTR), ("lpszClassName", LPCWSTR), ("hIconSm", HANDLE)] def PyWndProcedure(hWnd, Msg, wParam, lParam): if Msg == WM_DESTROY: windll.user32.PostQuitMessage(0) else: return windll.user32.DefWindowProcA(hWnd, Msg, wParam, lParam) return 0 WndProc = WNDPROCTYPE(PyWndProcedure) hInst = windll.kernel32.GetModuleHandleW(0) print(hInst) wclassName = u'My Python Win32 Class' wndClass = WNDCLASSEX() wndClass.cbSize = sizeof(WNDCLASSEX) wndClass.style = CS_HREDRAW | CS_VREDRAW wndClass.lpfnWndProc = WndProc wndClass.cbClsExtra = 0 wndClass.cbWndExtra = 0 wndClass.hInstance = hInst wndClass.hIcon = 0 wndClass.hCursor = 0 wndClass.hBrush = windll.gdi32.GetStockObject(WHITE_BRUSH) wndClass.lpszMenuName = 0 wndClass.lpszClassName = wclassName wndClass.hIconSm = 0 print(wndClass) regRes = windll.user32.RegisterClassExW(byref(wndClass)) print(regRes) wname = u'My test window' hWnd = windll.user32.CreateWindowExW( 0, wclassName, wname, WS_OVERLAPPEDWINDOW | WS_CAPTION, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, 0, 0, hInst, 0) print('hWnd', hWnd) if not hWnd: print('Failed to create window') exit(0) print('ShowWindow', windll.user32.ShowWindow(hWnd, SW_SHOW)) print('UpdateWindow', windll.user32.UpdateWindow(hWnd)) msg = MSG() lpmsg = pointer(msg) print('Entering message loop') while windll.user32.GetMessageA(lpmsg, 0, 0, 0) != 0: windll.user32.TranslateMessage(lpmsg) windll.user32.DispatchMessageA(lpmsg) print('done.')
It is because you are creating a Unicode window with CreateWindowExW but then calling the ANSI DefWindowProcA. You are passing Unicode strings which typically have zero for every other byte since your text is in the ASCII range which explains what you observe. The solution? Call DefWindowProcW instead. Actually, a better solution would be to use win32gui instead which wraps this up a bit more for you.
If you provide a regular string rather than an unicode string, the text is displayed properly. wname = 'My test window' It looks strange to me because you are using the Unicode API (CreateWindowExW). Maybe the root cause is somewhere else. I hope it helps
OS X and ctypes: how to retrieve raw pixels from CGDataProviderCopyData()?
I am trying to capture the screen using only the ctypes modules. Unfortunately I cannot retrieve raw pixel from CGDataProviderCopyData. I need to get an access to raw data: #!/usr/bin/env python # coding: utf-8 from sys import maxsize from ctypes import POINTER, Structure, c_double, byref, c_void_p, c_int32, c_uint32, c_float, cdll from ctypes.util import find_library CGFloat = c_double if maxsize > 2 ** 32 else c_float class CGPoint(Structure): _fields_ = [('x', CGFloat), ('y', CGFloat)] class CGSize(Structure): _fields_ = [('width', CGFloat), ('height', CGFloat)] class CGRect(Structure): _fields_ = [('origin', CGPoint), ('size', CGSize)] # Library cgs = cdll.LoadLibrary(find_library('CoreGraphics')) # Argtypes cgs.CGGetActiveDisplayList.argtypes = [c_uint32, POINTER(c_uint32), POINTER(c_uint32)] cgs.CGDisplayBounds.argtypes = [c_uint32] cgs.CGRectStandardize.argtypes = [CGRect] cgs.CGDisplayRotation.argtypes = [c_uint32] cgs.CGWindowListCreateImage.argtypes = [CGRect, c_uint32, c_uint32, c_uint32] cgs.CGImageGetWidth.argtypes = [c_void_p] cgs.CGImageGetHeight.argtypes = [c_void_p] cgs.CGImageGetDataProvider.argtypes = [c_void_p] cgs.CGDataProviderCopyData.argtypes = [c_void_p] cgs.CGDataProviderRelease.argtypes = [c_void_p] # Restypes cgs.CGGetActiveDisplayList.restype = c_int32 cgs.CGDisplayBounds.restype = CGRect cgs.CGRectStandardize.restype = CGRect cgs.CGDisplayRotation.restype = c_float cgs.CGWindowListCreateImage.restype = c_void_p cgs.CGImageGetWidth.restype = c_uint32 cgs.CGImageGetHeight.restype = c_uint32 cgs.CGImageGetDataProvider.restype = c_void_p cgs.CGDataProviderCopyData.restype = c_void_p cgs.CGDataProviderRelease.restype = c_void_p # Monitors max_displays = 32 display_count = c_uint32(0) active_displays = (c_uint32 * max_displays)() cgs.CGGetActiveDisplayList(max_displays, active_displays, byref(display_count)) for idx in range(display_count.value): display = active_displays[idx] rect = cgs.CGDisplayBounds(display) rect = cgs.CGRectStandardize(rect) image_ref = cgs.CGWindowListCreateImage(rect, 1, 0, 0) width = int(cgs.CGImageGetWidth(image_ref)) height = int(cgs.CGImageGetHeight(image_ref)) prov = cgs.CGImageGetDataProvider(image_ref) data = cgs.CGDataProviderCopyData(prov) # How to get raw pixels from data`? MacOS X version 10.11.3. Python versions 2.7.10 and 2.6.9.
I have no experience with the CoreGraphics API, but looking at the documentation it looks like CGDataProviderCopyData returns a CFDataRef object. The documentat for CFDataRef has a section on "Examining a CFData Object" which describes a CFDataGetLength function and a CFDataGetBytePtr function which return the length of the data and a UInt8*. https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGDataProvider/#//apple_ref/c/func/CGDataProviderCopyData https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFDataRef/index.html
Thanks to Snorfalorpagus, I finally succeed: #... data = cgs.CGDataProviderCopyData(prov) data_ref = cgs.CFDataGetBytePtr(data) buf_len = cgs.CFDataGetLength() image_data = cast(data_ref, POINTER(c_ubyte * buf_len)) cgs.CGDataProviderRelease(prov) # Raw pixels are in image_data.contents
Winapi GetDIBits access violation
I want to get the raw bytes of a BITMAPINFO in python. This is my complete code: import ctypes from ctypes import wintypes windll = ctypes.windll user32 = windll.user32 gdi32 = windll.gdi32 class RECT(ctypes.Structure): _fields_ = [ ('left', ctypes.c_long), ('top', ctypes.c_long), ('right', ctypes.c_long), ('bottom', ctypes.c_long) ] class BITMAPINFOHEADER(ctypes.Structure): _fields_ = [ ("biSize", wintypes.DWORD), ("biWidth", ctypes.c_long), ("biHeight", ctypes.c_long), ("biPlanes", wintypes.WORD), ("biBitCount", wintypes.WORD), ("biCompression", wintypes.DWORD), ("biSizeImage", wintypes.DWORD), ("biXPelsPerMeter", ctypes.c_long), ("biYPelsPerMeter", ctypes.c_long), ("biClrUsed", wintypes.DWORD), ("biClrImportant", wintypes.DWORD) ] class RGBQUAD(ctypes.Structure): _fields_ = [ ("rgbBlue", wintypes.BYTE), ("rgbGreen", wintypes.BYTE), ("rgbRed", wintypes.BYTE), ("rgbReserved", ctypes.c_void_p) ] class BITMAP(ctypes.Structure): _fields_ = [ ("bmType", ctypes.c_long), ("bmWidth", ctypes.c_long), ("bmHeight", ctypes.c_long), ("bmWidthBytes", ctypes.c_long), ("bmPlanes", wintypes.DWORD), ("bmBitsPixel", wintypes.DWORD), ("bmBits", ctypes.c_void_p) ] whandle = 327756 # Just a handle of an open application rect = RECT() user32.GetClientRect(whandle, ctypes.byref(rect)) # bbox = (rect.left, rect.top, rect.right, rect.bottom) hdcScreen = user32.GetDC(None) hdc = gdi32.CreateCompatibleDC(hdcScreen) hbmp = gdi32.CreateCompatibleBitmap( hdcScreen, rect.right - rect.left, rect.bottom - rect.top ) gdi32.SelectObject(hdc, hbmp) PW_CLIENTONLY = 1 if not user32.PrintWindow(whandle, hdc, PW_CLIENTONLY): raise Exception("PrintWindow failed") bmap = BITMAP() if not gdi32.GetObjectW(hbmp, ctypes.sizeof(BITMAP), ctypes.byref(bmap)): raise Exception("GetObject failed") class BITMAPINFO(ctypes.Structure): _fields_ = [ ("BITMAPINFOHEADER", BITMAPINFOHEADER), ("RGBQUAD", RGBQUAD * 1000) ] bminfo = BITMAPINFO() bminfo.BITMAPINFOHEADER.biSize = ctypes.sizeof(BITMAPINFOHEADER) bminfo.BITMAPINFOHEADER.biWidth = bmap.bmWidth bminfo.BITMAPINFOHEADER.biHeight = bmap.bmHeight bminfo.BITMAPINFOHEADER.biPlanes = bmap.bmPlanes bminfo.BITMAPINFOHEADER.biBitCount = bmap.bmBitsPixel bminfo.BITMAPINFOHEADER.biCompression = 0 bminfo.BITMAPINFOHEADER.biClrImportant = 0 out = ctypes.create_string_buffer(1000) if not gdi32.GetDIBits(hdc, hbmp, 0, bmap.bmHeight, None, bminfo, 0): raise Exception("GetDIBits failed") I need a way to know how long the array of RGBQUADS has to be in the BITMAPINFO struct and also the lenght of the out buffer. The 1000 is in there as a placeholder. gdi32.GetDIBits fails with an access violation. I guess it's because i have to have the array and buffer with the correct lenght. I post the whole source, because i don't know what's failing. Any help is appreciated. UPDATE corrected DWORDs in BITMAP being WORDs and a void pointer in RGBQUAD to be BYTE getting the size of image data: def round_up32(n): multiple = 32 while multiple < n: multiple += 32 return multiple data_len = round_up32(bmap.bmWidth * bmap.bmBitsPixel) * bmap.bmHeight Still getting access violation. I also saw that there is no RGBQUAD array for 32-bit-per-pixel bitmaps. Is that true?
What i had wrong: Structs (thanks David) : BITMAP has no DWORDs; it has WORDs. RGBQUADs rgbReserved is a BYTE not a void pointer. BITMAPINFO doesn't need RGBQUAD array for bitmaps with 32 bits per pixel. Pointers (thanks Roger, i come from python :P ): GetDIBits parameter lpvBits needs a pointer to the buffer GetDIBits parameter lpbi needs a pointer to the struct What i didn't know: How big the buffer had to be. Quoting Jonathan: Each row of the bitmap is bmWidth * bmBitsPixel bits in size, rounded up to the next multiple of 32 bits. Multiply the row length by bmHeight to calculate the total size of the image data. I came up with this: def round_up32(n): multiple = 32 while multiple < n: multiple += 32 return multiple scanline_len = round_up32(bmap.bmWidth * bmap.bmBitsPixel) data_len = scanline_len * bmap.bmHeight data_len is then used to initalize ctypes.create_string_buffer(). GetDIBits only returns pixel data, so i had to build the header. After making all this changes nothing failed but the image was inverted. I found that GetDIBits returns the scanlines inverted for compatibilty reasons. I made a new PIL Image from the bytes and then flipped it. The full source follows: import struct from PIL import Image from PIL.ImageOps import flip import ctypes from ctypes import wintypes windll = ctypes.windll user32 = windll.user32 gdi32 = windll.gdi32 class RECT(ctypes.Structure): _fields_ = [ ('left', ctypes.c_long), ('top', ctypes.c_long), ('right', ctypes.c_long), ('bottom', ctypes.c_long) ] class BITMAPINFOHEADER(ctypes.Structure): _fields_ = [ ("biSize", wintypes.DWORD), ("biWidth", ctypes.c_long), ("biHeight", ctypes.c_long), ("biPlanes", wintypes.WORD), ("biBitCount", wintypes.WORD), ("biCompression", wintypes.DWORD), ("biSizeImage", wintypes.DWORD), ("biXPelsPerMeter", ctypes.c_long), ("biYPelsPerMeter", ctypes.c_long), ("biClrUsed", wintypes.DWORD), ("biClrImportant", wintypes.DWORD) ] class BITMAPINFO(ctypes.Structure): _fields_ = [ ("bmiHeader", BITMAPINFOHEADER) ] class BITMAP(ctypes.Structure): _fields_ = [ ("bmType", ctypes.c_long), ("bmWidth", ctypes.c_long), ("bmHeight", ctypes.c_long), ("bmWidthBytes", ctypes.c_long), ("bmPlanes", wintypes.WORD), ("bmBitsPixel", wintypes.WORD), ("bmBits", ctypes.c_void_p) ] def get_window_image(whandle): def round_up32(n): multiple = 32 while multiple < n: multiple += 32 return multiple rect = RECT() user32.GetClientRect(whandle, ctypes.byref(rect)) bbox = (rect.left, rect.top, rect.right, rect.bottom) hdcScreen = user32.GetDC(None) hdc = gdi32.CreateCompatibleDC(hdcScreen) hbmp = gdi32.CreateCompatibleBitmap( hdcScreen, bbox[2] - bbox[0], bbox[3] - bbox[1] ) gdi32.SelectObject(hdc, hbmp) PW_CLIENTONLY = 1 if not user32.PrintWindow(whandle, hdc, PW_CLIENTONLY): raise Exception("PrintWindow failed") bmap = BITMAP() if not gdi32.GetObjectW(hbmp, ctypes.sizeof(BITMAP), ctypes.byref(bmap)): raise Exception("GetObject failed") if bmap.bmBitsPixel != 32: raise Exception("WTF") scanline_len = round_up32(bmap.bmWidth * bmap.bmBitsPixel) data_len = scanline_len * bmap.bmHeight # http://msdn.microsoft.com/en-us/library/ms969901.aspx bminfo = BITMAPINFO() bminfo.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER) bminfo.bmiHeader.biWidth = bmap.bmWidth bminfo.bmiHeader.biHeight = bmap.bmHeight bminfo.bmiHeader.biPlanes = 1 bminfo.bmiHeader.biBitCount = 24 # bmap.bmBitsPixel bminfo.bmiHeader.biCompression = 0 data = ctypes.create_string_buffer(data_len) DIB_RGB_COLORS = 0 get_bits_success = gdi32.GetDIBits( hdc, hbmp, 0, bmap.bmHeight, ctypes.byref(data), ctypes.byref(bminfo), DIB_RGB_COLORS ) if not get_bits_success: raise Exception("GetDIBits failed") # http://msdn.microsoft.com/en-us/library/dd183376%28v=vs.85%29.aspx bmiheader_fmt = "LllHHLLllLL" unpacked_header = [ bminfo.bmiHeader.biSize, bminfo.bmiHeader.biWidth, bminfo.bmiHeader.biHeight, bminfo.bmiHeader.biPlanes, bminfo.bmiHeader.biBitCount, bminfo.bmiHeader.biCompression, bminfo.bmiHeader.biSizeImage, bminfo.bmiHeader.biXPelsPerMeter, bminfo.bmiHeader.biYPelsPerMeter, bminfo.bmiHeader.biClrUsed, bminfo.bmiHeader.biClrImportant ] # Indexes: biXPelsPerMeter = 7, biYPelsPerMeter = 8 # Value from https://stackoverflow.com/a/23982267/2065904 unpacked_header[7] = 3779 unpacked_header[8] = 3779 image_header = struct.pack(bmiheader_fmt, *unpacked_header) image = image_header + data return flip(Image.frombytes("RGB", (bmap.bmWidth, bmap.bmHeight), image)) Pass a window handle (int) to get_window_image() and it returns a PIL image. The only issue is that the colors are... weird? I'll figure that out another time.
The following code is wrong def round_up32(n): multiple = 32 while multiple < n: multiple += 32 return multiple scanline_len = round_up32(bmap.bmWidth * bmap.bmBitsPixel) data_len = scanline_len * bmap.bmHeight You are trying to find the number of bytes to create an array or simply allocate a block of memory. Memory is always bytes. So bmap.bmBitsPixel should be converted to bytes. 32 bits is 4 bytes. Since you are already checking bmap.bmBitsPixel for 32 bits replace bmap.bmBitsPixel with 4 and remove round_up32 function. scanline_len = bmap.bmWidth * 4 data_len = scanline_len * bmap.bmHeight
rsvg python memory leak in osx (ctypes?)
I am using the following code to read an svg: from ctypes import CDLL, POINTER, Structure, byref, util from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p class _PycairoContext(Structure): _fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__), ("ctx", c_void_p), ("base", c_void_p)] class _RsvgProps(Structure): _fields_ = [("width", c_int), ("height", c_int), ("em", c_double), ("ex", c_double)] class _GError(Structure): _fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)] def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None): if rsvg_lib_path is None: rsvg_lib_path = util.find_library('rsvg-2') if gobject_lib_path is None: gobject_lib_path = util.find_library('gobject-2.0') l = CDLL(rsvg_lib_path) g = CDLL(gobject_lib_path) g.g_type_init() l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))] l.rsvg_handle_new_from_file.restype = c_void_p l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p] l.rsvg_handle_render_cairo.restype = c_bool l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)] return l _librsvg = _load_rsvg() class Handle(object): def __init__(self, path): lib = _librsvg err = POINTER(_GError)() self.handle = lib.rsvg_handle_new_from_file(path, byref(err)) if self.handle is None: gerr = err.contents raise Exception(gerr.message) self.props = _RsvgProps() lib.rsvg_handle_get_dimensions(self.handle, byref(self.props)) def render_cairo(self, ctx): """Returns True is drawing succeeded.""" z = _PycairoContext.from_address(id(ctx)) return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx) (from Error with Python ctypes and librsvg) I call img = Handle(path) and this leaks memory. I am pretty sure this is due to incorrectly using ctypes & pointers, but I cannot find a way to fix this.
According to the documentation of rsvg_handle_new, free the handle with g_object_unref. Also, if a failed call allocates a GError, after you get the code and message you have to free the error with g_error_free. from ctypes import * from ctypes.util import find_library _gobj = CDLL(find_library("gobject-2.0")) _glib = CDLL(find_library("glib-2.0")) class _GError(Structure): _fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)] _GErrorP = POINTER(_GError) _glib.g_error_free.restype = None _glib.g_error_free.argtypes = [_GErrorP] _gobj.g_object_unref.restype = None _gobj.g_object_unref.argtypes = [c_void_p] You can free the handle in the __del__ method of the Handle class: class Handle(object): _gobj = _gobj # keep a valid ref for module teardown # ... def __del__(self): if self.handle: self._gobj.g_object_unref(self.handle) self.handle = None