如何用Python的ctypes获取Windows会话ID并修复访问违例错误
用Python ctypes获取Windows当前会话ID时出现访问违例错误
我尝试不依赖外部库,用Python的ctypes获取Windows当前用户会话的Session ID,写了以下代码,但运行时出现access violation reading错误,怀疑是内存访问或ctypes使用不当导致,希望找出问题并修正,同时了解其他实现方案。
我的代码:
import ctypes from ctypes import wintypes def get_current_session_id(): WTS_CURRENT_SERVER_HANDLE = ctypes.wintypes.HANDLE(-1) WTS_CURRENT_SESSION = 0xFFFFFFFF session_id = ctypes.create_string_buffer(4) # Assuming a DWORD is 4 bytes server_handle = ctypes.windll.wtsapi32.WTSOpenServerW(None) if not server_handle: raise OSError("Failed to open server handle") try: result = ctypes.windll.wtsapi32.WTSQuerySessionInformationW( server_handle, WTS_CURRENT_SESSION, 0, ctypes.byref(session_id), ctypes.byref(ctypes.c_ulong()) ) if not result: raise OSError("Failed to query session information") finally: ctypes.windll.wtsapi32.WTSCloseServer(server_handle) session_id_value = struct.unpack("I", session_id.raw)[0] return session_id_value try: session_id = get_current_session_id() print("Current session ID:", session_id) except OSError as e: print(f"Error: {e}")
操作与结果:
- 尝试操作:运行上述脚本获取Windows当前用户会话ID
- 预期结果:成功获取并输出正确的Session ID,无错误
- 实际结果:执行时触发access violation reading错误
问题分析
- WTSQuerySessionInformationW参数误用:
- 第3个参数是
WTS_INFO_CLASS枚举,传入的0对应WTSInitialProgram,并非获取会话ID的枚举值;正确值应为WTSSessionId(数值10)。 - 该API会自动分配内存存储返回数据,不能传入自定义buffer,否则会触发内存访问冲突。
- 第5个参数需传入有效的
ULONG变量接收返回数据长度,临时创建的ctypes.c_ulong()会导致API写入无效内存。
- 第3个参数是
- 缺少内存释放:WTSQuerySessionInformationW分配的内存必须用
WTSFreeMemory释放,否则会造成内存泄漏。 - 不必要的服务器句柄调用:查询本地服务器会话信息时,直接使用
WTS_CURRENT_SERVER_HANDLE(值为-1)即可,无需调用WTSOpenServerW。 - 缺失模块导入:代码中使用
struct.unpack但未导入struct模块。
修正后的代码
import ctypes from ctypes import wintypes def get_current_session_id(): # 定义常量 WTS_CURRENT_SERVER_HANDLE = wintypes.HANDLE(-1) WTS_CURRENT_SESSION = 0xFFFFFFFF WTS_SESSION_ID = 10 # WTS_INFO_CLASS枚举中的WTSSessionId # 加载库并设置函数参数/返回值类型 wtsapi32 = ctypes.WinDLL("wtsapi32") wtsapi32.WTSQuerySessionInformationW.argtypes = [ wintypes.HANDLE, wintypes.DWORD, wintypes.DWORD, ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(wintypes.DWORD) ] wtsapi32.WTSQuerySessionInformationW.restype = wintypes.BOOL wtsapi32.WTSFreeMemory.argtypes = [ctypes.c_void_p] wtsapi32.WTSFreeMemory.restype = None session_ptr = ctypes.c_void_p() bytes_returned = wintypes.DWORD() result = wtsapi32.WTSQuerySessionInformationW( WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTS_SESSION_ID, ctypes.byref(session_ptr), ctypes.byref(bytes_returned) ) if not result: raise OSError(f"Query failed: {ctypes.GetLastError()}") try: # 转换指针获取会话ID session_id = ctypes.cast(session_ptr, ctypes.POINTER(wintypes.DWORD)).contents.value return session_id finally: # 释放API分配的内存 wtsapi32.WTSFreeMemory(session_ptr) try: session_id = get_current_session_id() print("Current session ID:", session_id) except OSError as e: print(f"Error: {e}")
其他实现方案
方案1:使用WTSGetActiveConsoleSessionId(最简)
直接获取当前控制台会话ID,无需复杂参数:
import ctypes from ctypes import wintypes def get_console_session_id(): kernel32 = ctypes.WinDLL("kernel32") kernel32.WTSGetActiveConsoleSessionId.argtypes = [] kernel32.WTSGetActiveConsoleSessionId.restype = wintypes.DWORD return kernel32.WTSGetActiveConsoleSessionId() print("Console session ID:", get_console_session_id())
方案2:通过进程令牌获取会话ID
适用于需要验证用户会话的场景:
import ctypes from ctypes import wintypes def get_session_id_from_token(): kernel32 = ctypes.WinDLL("kernel32") advapi32 = ctypes.WinDLL("advapi32") # 定义常量 TOKEN_QUERY = 0x0008 TokenSessionId = 11 # TOKEN_INFORMATION_CLASS枚举值 # 获取当前进程句柄 process_handle = kernel32.GetCurrentProcess() if not process_handle: raise OSError(f"Failed to get process handle: {ctypes.GetLastError()}") token_handle = wintypes.HANDLE() # 打开进程令牌 result = advapi32.OpenProcessToken( process_handle, TOKEN_QUERY, ctypes.byref(token_handle) ) if not result: raise OSError(f"Failed to open process token: {ctypes.GetLastError()}") try: session_id = wintypes.DWORD() return_length = wintypes.DWORD() # 获取令牌中的会话ID result = advapi32.GetTokenInformation( token_handle, TokenSessionId, ctypes.byref(session_id), ctypes.sizeof(session_id), ctypes.byref(return_length) ) if not result: raise OSError(f"Failed to get token information: {ctypes.GetLastError()}") return session_id.value finally: # 关闭令牌句柄 kernel32.CloseHandle(token_handle) try: session_id = get_session_id_from_token() print("Current session ID (from token):", session_id) except OSError as e: print(f"Error: {e}")
内容的提问来源于stack exchange,提问作者zen2818
相关产品推荐
相关产品推荐

