Python ctypes Structure类找不到dwTime属性且返回值恒为0问题
问题根因分析
两个问题都是使用ctypes定义结构体、调用Windows API时的典型低级错误,具体原因如下:
问题1:抛出AttributeError,提示实例不存在dwTime属性
由两个基础语法/规则错误直接导致:
- ctypes强制规定,
Structure子类的C结构体字段映射必须定义在带单下划线前缀的_fields_类属性中,原代码写的是fields_,开头缺失下划线,完全不符合ctypes的识别规则。 - 原代码中
fields_的缩进完全错误,定义位置和class LASTINPUTINFO块同级,没有缩进在类内部,根本不属于类的属性。ctypes读不到结构体字段定义,自然不会为实例生成cbSize、dwTime这些C结构体映射属性。代码中手动给实例赋值cbSize只是给Python对象动态添加了一个普通属性,既不会写入C结构体对应的内存,也不会自动生成dwTime属性,访问时直接报错。
问题2:修复属性错误后,输出间隔恒为0毫秒
这个问题是ctypes调用WinAPI时未显式声明类型签名导致的:
- ctypes默认不会校验WinAPI的参数和返回值类型,所有返回值默认按32位有符号整数
c_int解析,参数也不会自动做指针类型校验。在64位Python环境下,这种隐式调用很容易出现传参内存对齐错误、返回值解析错误,导致GetLastInputInfo调用失败,不会往结构体里写入正确的dwTime值。 GetLastInputInfo调用失败时不会修改传入的结构体内容,加上默认内存值、返回值解析错误的共同影响,就会出现dwTime和GetTickCount返回值相等、差值恒为0的情况。
修复步骤
- 修正
LASTINPUTINFO类的定义:给字段属性加前导下划线_fields_,将其缩进至类内部,保证字段类型和Windows API定义的结构体完全匹配。 - 显式声明调用的两个WinAPI的参数类型
argtypes和返回值类型restype,避免ctypes隐式类型转换导致的调用失败。 - 增加API调用失败的判断逻辑,方便排查环境问题。
修复后的可运行代码
from ctypes import byref, c_uint, c_ulong, sizeof, Structure, windll, POINTER, c_bool import time class LASTINPUTINFO(Structure): _fields_ = [ ('cbSize', c_uint), ('dwTime', c_ulong) ] # 显式声明API类型签名,规避隐式调用错误 windll.user32.GetLastInputInfo.argtypes = [POINTER(LASTINPUTINFO)] windll.user32.GetLastInputInfo.restype = c_bool windll.kernel32.GetTickCount.restype = c_ulong def get_last_input(): struct_lastinputinfo = LASTINPUTINFO() struct_lastinputinfo.cbSize = sizeof(LASTINPUTINFO) if not windll.user32.GetLastInputInfo(byref(struct_lastinputinfo)): print("[-] GetLastInputInfo调用失败") return -1 run_time = windll.kernel32.GetTickCount() elapsed = run_time - struct_lastinputinfo.dwTime print(f"[*] 距上次输入事件已过去 {elapsed} 毫秒") return elapsed if __name__ == "__main__": while True: get_last_input() time.sleep(1)
补充说明
如果修复代码后运行仍然恒输出0,说明当前运行环境是非交互式会话(比如系统服务进程、沙箱/虚拟机环境Hook了相关API),这类环境本身没有物理用户输入设备,GetLastInputInfo会直接返回当前tick count,属于环境问题而非代码错误。
内容的提问来源于stack exchange,提问作者Алекс Шевцов
相关产品推荐
相关产品推荐

