Related
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:
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
I'm trying to create a new Windows window by calling the win32gui.CreateWindow Function. The Window is created as expected, however the title is made up of Chinese (or other similar) characters, instead of reading "Hello, World!".
By counting the number of characters and reading documentation, I guessed that windows is using double width characters and python is not. Here are some of the things I tried to make it display properly, none of these attempts worked however:
def to_16(s):
s = s.encode('utf-16')
return "".join([format(i, "X").rjust(2,"0") for i in s])
def to_16(s):
return ''.join([f'{c}{chr(32)}' for c in s])
def to_16(s):
return ''.join([chr(i) for i in s.encode('utf-16')])
This is the code I use to create the window:
import ctypes
import cython
import win32, win32gui, win32ui, win32con, win32api
LRESULT = ctypes.c_longlong
WNDPROC = ctypes.CFUNCTYPE(LRESULT,
ctypes.wintypes.HWND,
ctypes.c_uint,
ctypes.wintypes.WPARAM,
ctypes.wintypes.LPARAM)
#WNDPROC
def WindowProc(hWnd:ctypes.wintypes.HWND,
uMsg:ctypes.c_uint,
wParam:ctypes.wintypes.WPARAM,
lParam:ctypes.wintypes.LPARAM)->LRESULT:
if uMsg == win32con.WM_PAINT:
hDC, paintStruct = win32gui.BeginPaint(hWnd)
rect = win32gui.GetClientRect(hWnd)
win32gui.DrawText(hDC,
'Hello send by Python via Win32!',
-1,
rect,
win32con.DT_SINGLELINE | win32con.DT_CENTER | win32con.DT_VCENTER)
win32gui.EndPaint(hWnd, paintStruct)
return 0
elif uMsg == win32con.WM_DESTROY:
print('Window: destroyed')
win32gui.PostQuitMessage(0)
return 0
else:
return win32gui.DefWindowProc(hWnd, uMsg, wParam, lParam)
HCURSOR = ctypes.wintypes.HICON
class WNDCLASSEX(ctypes.Structure): # tagWNDCLASSEXA
_fields_ = [("cbSize" , ctypes.c_uint),
("style" , ctypes.c_uint),
("lpfnWndProc" , WNDPROC),
("cbClsExtra" , ctypes.c_int),
("cbWndExtra" , ctypes.c_int),
("hInstance" , ctypes.wintypes.HINSTANCE),
("hIcon" , ctypes.wintypes.HICON),
("hCursor" , HCURSOR),
("hbrBackground", ctypes.wintypes.HBRUSH),
("lpszMenuName" , ctypes.wintypes.LPCWSTR),
("lpszClassName", ctypes.wintypes.LPCWSTR),
("hIconSm" , ctypes.wintypes.HICON),]
def WinMain():
print('Start...')
hInstance = win32api.GetModuleHandle()
hPrevInstance = None
lpCmdLine = ''
nCmdShow = win32con.SW_SHOWNORMAL
szWindowClass = 'DesktopApp' # The main window class name.
szTitle = 'Hello, World!' # The string that appears in the application's title bar.
wcex = WNDCLASSEX()
wcex.cbSize = ctypes.sizeof(WNDCLASSEX)
wcex.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW
wcex.lpfnWndProc = WindowProc
wcex.cbClsExtra = 0
wcex.cbWndExtra = 0
wcex.hInstance = hInstance
wcex.hIcon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
wcex.hCursor = win32api.LoadCursor(None, win32con.IDC_ARROW)
wcex.hbrBackground = ctypes.wintypes.HBRUSH(win32con.COLOR_WINDOW+1)
wcex.lpszMenuName = None
wcex.lpszClassName = szWindowClass
wcex.hIconSm = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
print('Setup: done')
wndClassAtom = 0
try:
wndClassAtom = ctypes.windll.user32.RegisterClassExA(wcex)
print(f'wndClassAtom: {wndClassAtom}')
except Exception as e:
print(e)
finally:
if wndClassAtom == 0:
print('Call to RegisterClassEx failed!')
return 1
print('Class: registered')
hInst = hInstance
hWnd = 0
try:
hWnd = win32gui.CreateWindow(wndClassAtom,
to_16(szTitle),
win32con.WS_OVERLAPPEDWINDOW,
win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
500, 100,
0,
0,
hInstance,
None)
print(f'hWnd: {hWnd}')
except Exception as e:
print(e)
finally:
if not hWnd:
win32gui.UnregisterClass(wndClassAtom, hInstance)
print('Call to CreateWindow failed!')
return 1
print('Window: created')
win32gui.ShowWindow(hWnd,
nCmdShow)
win32gui.UpdateWindow(hWnd)
# Dispatch messages
win32gui.PumpMessages()
win32gui.UnregisterClass(wndClassAtom, hInstance)
return 0
errCode = WinMain()
if errCode == 1: print('----------WINDOWS ERROR----------')
I'd love for a robust way to pass python strings to the Windows API.
I've been trying to figure out a way to get some sort of ability to be able to return the true abspath of a symbolic link in Windows, under Python 2.7. (I cannot upgrade to 3.x, as most DCCs such as Maya/3ds max do not use that version of Python)
I've looked at the sid0 ntfs utils (whose islink() function works, but readlink() function always returns an empty unicode string for me for some reason), and the juntalis ntfs libs (which unfortunately, I couldn't get to work), along with a helpful script someone posted:
import os, ctypes, struct
from ctypes import windll, wintypes
FSCTL_GET_REPARSE_POINT = 0x900a8
FILE_ATTRIBUTE_READONLY = 0x0001
FILE_ATTRIBUTE_HIDDEN = 0x0002
FILE_ATTRIBUTE_DIRECTORY = 0x0010
FILE_ATTRIBUTE_NORMAL = 0x0080
FILE_ATTRIBUTE_REPARSE_POINT = 0x0400
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 3
FILE_READ_ATTRIBUTES = 0x80
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF
FILE_FLAG_OPEN_REPARSE_POINT = 2097152
FILE_FLAG_BACKUP_SEMANTICS = 33554432
# FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTI
FILE_FLAG_REPARSE_BACKUP = 35651584
GetFileAttributes = windll.kernel32.GetFileAttributesW
_CreateFileW = windll.kernel32.CreateFileW
_DevIoCtl = windll.kernel32.DeviceIoControl
_DevIoCtl.argtypes = [
wintypes.HANDLE, #HANDLE hDevice
wintypes.DWORD, #DWORD dwIoControlCode
wintypes.LPVOID, #LPVOID lpInBuffer
wintypes.DWORD, #DWORD nInBufferSize
wintypes.LPVOID, #LPVOID lpOutBuffer
wintypes.DWORD, #DWORD nOutBufferSize
ctypes.POINTER(wintypes.DWORD), #LPDWORD lpBytesReturned
wintypes.LPVOID] #LPOVERLAPPED lpOverlapped
_DevIoCtl.restype = wintypes.BOOL
def islink(path):
# assert os.path.isdir(path), path
if GetFileAttributes(path) & FILE_ATTRIBUTE_REPARSE_POINT:
return True
else:
return False
def DeviceIoControl(hDevice, ioControlCode, input, output):
# DeviceIoControl Function
# http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx
if input:
input_size = len(input)
else:
input_size = 0
if isinstance(output, int):
output = ctypes.create_string_buffer(output)
output_size = len(output)
assert isinstance(output, ctypes.Array)
bytesReturned = wintypes.DWORD()
status = _DevIoCtl(hDevice, ioControlCode, input,
input_size, output, output_size, bytesReturned, None)
print "status(%d)" % status
if status != 0:
return output[:bytesReturned.value]
else:
return None
def CreateFile(path, access, sharemode, creation, flags):
return _CreateFileW(path, access, sharemode, None, creation, flags, None)
SymbolicLinkReparseFormat = "LHHHHHHL"
SymbolicLinkReparseSize = struct.calcsize(SymbolicLinkReparseFormat);
def readlink(path):
""" Windows readlink implementation. """
# This wouldn't return true if the file didn't exist, as far as I know.
assert islink(path)
# assert type(path) == unicode
# Open the file correctly depending on the string type.
hfile = CreateFile(path, GENERIC_READ, 0, OPEN_EXISTING,
FILE_FLAG_REPARSE_BACKUP)
# MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024)
buffer = DeviceIoControl(hfile, FSCTL_GET_REPARSE_POINT, None, 16384)
windll.CloseHandle(hfile)
# Minimum possible length (assuming length of the target is bigger than 0)
if not buffer or len(buffer) < 9:
return None
# Only handle SymbolicLinkReparseBuffer
(tag, dataLength, reserver, SubstituteNameOffset, SubstituteNameLength,
PrintNameOffset, PrintNameLength,
Flags) = struct.unpack(SymbolicLinkReparseFormat,
buffer[:SymbolicLinkReparseSize])
print tag, dataLength, reserver, SubstituteNameOffset, SubstituteNameLength
start = SubstituteNameOffset + SymbolicLinkReparseSize
actualPath = buffer[start : start + SubstituteNameLength].decode("utf-16")
# This utf-16 string is null terminated
index = actualPath.find(u"\0")
assert index > 0
if index > 0:
actualPath = actualPath[:index]
if actualPath.startswith(u"?\\"):
return actualPath[2:]
else:
return actualPath
However, most of the time various solutions I've tried end up giving me:
[Error 126] The specified module could not be found
Even though I am able to import ctypes and do stuff such as importing cdll:
libc = cdll.msvcrt
libc.printf
<_FuncPtr object at 0x0000000002A9F388>
I'm pretty new to this part of Python and ctypes in general, so any pointers on dealing with symbolic links here would be very much appreciated!
ERROR_MOD_NOT_FOUND (126) is likely due to windll.CloseHandle(hfile), which tries to load "closehandle.dll". It's missing kernel32.
Here's an alternate implementation that handles junctions as well as symbolic links.
ctypes definitions
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
FILE_READ_ATTRIBUTES = 0x0080
OPEN_EXISTING = 3
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_ATTRIBUTE_REPARSE_POINT = 0x0400
IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
IO_REPARSE_TAG_SYMLINK = 0xA000000C
FSCTL_GET_REPARSE_POINT = 0x000900A8
MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 0x4000
LPDWORD = ctypes.POINTER(wintypes.DWORD)
LPWIN32_FIND_DATA = ctypes.POINTER(wintypes.WIN32_FIND_DATAW)
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
def IsReparseTagNameSurrogate(tag):
return bool(tag & 0x20000000)
def _check_invalid_handle(result, func, args):
if result == INVALID_HANDLE_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
return args
def _check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
kernel32.FindFirstFileW.errcheck = _check_invalid_handle
kernel32.FindFirstFileW.restype = wintypes.HANDLE
kernel32.FindFirstFileW.argtypes = (
wintypes.LPCWSTR, # _In_ lpFileName
LPWIN32_FIND_DATA) # _Out_ lpFindFileData
kernel32.FindClose.argtypes = (
wintypes.HANDLE,) # _Inout_ hFindFile
kernel32.CreateFileW.errcheck = _check_invalid_handle
kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.CreateFileW.argtypes = (
wintypes.LPCWSTR, # _In_ lpFileName
wintypes.DWORD, # _In_ dwDesiredAccess
wintypes.DWORD, # _In_ dwShareMode
wintypes.LPVOID, # _In_opt_ lpSecurityAttributes
wintypes.DWORD, # _In_ dwCreationDisposition
wintypes.DWORD, # _In_ dwFlagsAndAttributes
wintypes.HANDLE) # _In_opt_ hTemplateFile
kernel32.CloseHandle.argtypes = (
wintypes.HANDLE,) # _In_ hObject
kernel32.DeviceIoControl.errcheck = _check_bool
kernel32.DeviceIoControl.argtypes = (
wintypes.HANDLE, # _In_ hDevice
wintypes.DWORD, # _In_ dwIoControlCode
wintypes.LPVOID, # _In_opt_ lpInBuffer
wintypes.DWORD, # _In_ nInBufferSize
wintypes.LPVOID, # _Out_opt_ lpOutBuffer
wintypes.DWORD, # _In_ nOutBufferSize
LPDWORD, # _Out_opt_ lpBytesReturned
wintypes.LPVOID) # _Inout_opt_ lpOverlapped
class REPARSE_DATA_BUFFER(ctypes.Structure):
class ReparseData(ctypes.Union):
class LinkData(ctypes.Structure):
_fields_ = (('SubstituteNameOffset', wintypes.USHORT),
('SubstituteNameLength', wintypes.USHORT),
('PrintNameOffset', wintypes.USHORT),
('PrintNameLength', wintypes.USHORT))
#property
def PrintName(self):
dt = wintypes.WCHAR * (self.PrintNameLength //
ctypes.sizeof(wintypes.WCHAR))
name = dt.from_address(ctypes.addressof(self.PathBuffer) +
self.PrintNameOffset).value
if name.startswith(r'\??'):
name = r'\\?' + name[3:] # NT => Windows
return name
class SymbolicLinkData(LinkData):
_fields_ = (('Flags', wintypes.ULONG),
('PathBuffer', wintypes.BYTE * 0))
class MountPointData(LinkData):
_fields_ = (('PathBuffer', wintypes.BYTE * 0),)
class GenericData(ctypes.Structure):
_fields_ = (('DataBuffer', wintypes.BYTE * 0),)
_fields_ = (('SymbolicLinkReparseBuffer', SymbolicLinkData),
('MountPointReparseBuffer', MountPointData),
('GenericReparseBuffer', GenericData))
_fields_ = (('ReparseTag', wintypes.ULONG),
('ReparseDataLength', wintypes.USHORT),
('Reserved', wintypes.USHORT),
('ReparseData', ReparseData))
_anonymous_ = ('ReparseData',)
functions
def islink(path):
data = wintypes.WIN32_FIND_DATAW()
kernel32.FindClose(kernel32.FindFirstFileW(path, ctypes.byref(data)))
if not data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT:
return False
return IsReparseTagNameSurrogate(data.dwReserved0)
def readlink(path):
n = wintypes.DWORD()
buf = (wintypes.BYTE * MAXIMUM_REPARSE_DATA_BUFFER_SIZE)()
flags = FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS
handle = kernel32.CreateFileW(path, FILE_READ_ATTRIBUTES, 0, None,
OPEN_EXISTING, flags, None)
try:
kernel32.DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, None, 0,
buf, ctypes.sizeof(buf), ctypes.byref(n), None)
finally:
kernel32.CloseHandle(handle)
rb = REPARSE_DATA_BUFFER.from_buffer(buf)
tag = rb.ReparseTag
if tag == IO_REPARSE_TAG_SYMLINK:
return rb.SymbolicLinkReparseBuffer.PrintName
if tag == IO_REPARSE_TAG_MOUNT_POINT:
return rb.MountPointReparseBuffer.PrintName
if not IsReparseTagNameSurrogate(tag):
raise ValueError("not a link")
raise ValueError("unsupported reparse tag: %d" % tag)
example
>>> sys.version
'2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:53:40)
[MSC v.1500 64 bit (AMD64)]'
>>> os.system(r'mklink /d spam C:\Windows')
symbolic link created for spam <<===>> C:\Windows
0
>>> islink('spam')
True
>>> readlink('spam')
u'C:\\Windows'
>>> islink('C:/Documents and Settings') # junction
True
>>> readlink('C:/Documents and Settings')
u'C:\\Users'
>>> islink('C:/Users/All Users') # symlinkd
True
>>> readlink('C:/Users/All Users')
u'C:\\ProgramData'
This is implemented in Tcl as file readlink and the implementation of this might be worth reading as there seem to be a few differences. The WinReadLinkDirectory function calls NativeReadReparse to read the REPARSE_DATA_BUFFER but this uses different flags to the CreateFile function. Also the buffer size is different I think and the size of structures in Win32 calls is often used to detect the version of the API used so it is likely worth taking care to set the size to the correct value (or possibly the same value used in the Tcl implementation).
Just to show what I mean by Tcl support for this:
C:\>dir
Directory of C:\
22/09/2014 14:29 <JUNCTION> Code [C:\src]
...
C:\>tclsh
% file readlink Code
C:\src
I want to prevent windows (xp and 7) from shutting down.
For that it seems I have to intercept the WM_QUERYENDSESSION message and return False.
Ok I have to correct my statement, this program works fine under Windows 7 but on Windows 7 embedded Standard Service Pack 1 it does not prevent the shutdown!?
import ctypes
from ctypes import c_long, c_int, c_wchar_p
import time
import win32api
import win32con
import win32gui
import win32process
oldWndProc = None
def wndproc(hWnd, msg, wParam, lParam):
print("wndproc received message: %s" % msg)
if (msg == win32con.WM_QUERYENDSESSION):
print("wndproc received WM_QUERYENDSESSION")
return 0
# Pass all messages to the original WndProc
return win32gui.CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam)
if __name__ == "__main__":
try:
# Create a window just to be able to receive WM_QUERYENDSESSION messages
win32api.SetLastError(0)
hinst = win32api.GetModuleHandle(None)
messageMap = {}
wndclass = win32gui.WNDCLASS()
wndclass.hInstance = hinst
wndclass.lpszClassName = "PreventShutdownWindowClass"
wndclass.lpfnWndProc = messageMap
myWindowClass = win32gui.RegisterClass(wndclass)
hwnd = win32gui.CreateWindowEx(win32con.WS_EX_LEFT,
myWindowClass,
"PreventShutdownWindow",
0,
0,
0,
win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT,
0,
0,
hinst,
None)
print 'CreateWindowEx: ' + str(hwnd)
print 'CreateWindowEx last error: ' + str(win32api.GetLastError())
print ''
# Set WndProc
win32api.SetLastError(0)
WndProcType = ctypes.WINFUNCTYPE(c_int, c_long, c_int, c_int, c_int)
newWndProc = WndProcType(wndproc)
oldWndProc = ctypes.windll.user32.SetWindowLongW( hwnd, win32con.GWL_WNDPROC, newWndProc )
print 'SetWindowLong: ' + str(oldWndProc)
print 'SetWindowLong last error: ' + str(win32api.GetLastError())
print ''
# We provide a reason for the shutdown prevention
win32api.SetLastError(0)
ret = ctypes.windll.user32.ShutdownBlockReasonCreate(hwnd, c_wchar_p("PreventShutdown running which prevents shutdown"))
print 'ShutdownBlockReasonCreate: ' + str(ret)
print 'ShutdownBlockReasonCreatelast error: ' + str(win32api.GetLastError())
print ''
# We elevate the program to be asked as soon as possible to inhibit shutdown
win32api.SetLastError(0)
win32process.SetProcessShutdownParameters(0x4FF, win32con.SHUTDOWN_NORETRY)
print 'SetProcessShutdownParameters last error: ' + str(win32api.GetLastError())
print ''
except Exception, e:
print("Exception: %s" % str(e))
while True:
win32gui.PumpWaitingMessages()
time.sleep(1)