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

Categories

Resources