You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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错误

问题分析

  1. WTSQuerySessionInformationW参数误用:
    • 第3个参数是WTS_INFO_CLASS枚举,传入的0对应WTSInitialProgram,并非获取会话ID的枚举值;正确值应为WTSSessionId(数值10)。
    • 该API会自动分配内存存储返回数据,不能传入自定义buffer,否则会触发内存访问冲突。
    • 第5个参数需传入有效的ULONG变量接收返回数据长度,临时创建的ctypes.c_ulong()会导致API写入无效内存。
  2. 缺少内存释放:WTSQuerySessionInformationW分配的内存必须用WTSFreeMemory释放,否则会造成内存泄漏。
  3. 不必要的服务器句柄调用:查询本地服务器会话信息时,直接使用WTS_CURRENT_SERVER_HANDLE(值为-1)即可,无需调用WTSOpenServerW。
  4. 缺失模块导入:代码中使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 22:42:03