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

调用WTSEnumerateSessionsExA枚举终端服务会话触发AccessViolationException

解决WTSEnumerateSessionsExA调用时的AccessViolationException问题

你遇到的AccessViolationException主要是因为结构体定义不符合原生类型布局、P/Invoke函数参数声明错误这两个核心问题导致的,下面一步步拆解并修正:

1. 修正WTS_SESSION_INFO_1A结构体的定义

原生的WTS_SESSION_INFO_1A是C风格结构体,C#中必须严格匹配其内存布局和成员类型:

  • 添加[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]属性,确保成员顺序和字符集与原生一致(A版本函数对应ANSI字符集)
  • 字符串成员不能直接用string,原生返回的是char*指针,需要用IntPtr存储,之后手动通过Marshal.PtrToStringAnsi转换
  • 成员名称建议和原生结构体保持一致,便于对照官方文档

修正后的结构体:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct WTS_SESSION_INFO_1A
{
    public uint ExecEnvId;
    public WTS_CONNECT_STATE_CLASS State;
    public uint SessionId;
    public IntPtr pSessionName;
    public IntPtr pHostName;
    public IntPtr pUserName;
    public IntPtr pDomainName;
    public IntPtr pFarmName;
}

2. 修正WTS_CONNECT_STATE_CLASS枚举

虽然C#枚举默认从0开始,但显式指定每个值的数值更安全,避免和原生定义不一致:

enum WTS_CONNECT_STATE_CLASS
{
    WTSActive = 0,
    WTSConnected = 1,
    WTSConnectQuery = 2,
    WTSShadow = 3,
    WTSDisconnected = 4,
    WTSIdle = 5,
    WTSListen = 6,
    WTSReset = 7,
    WTSDown = 8,
    WTSInit = 9
}

3. 修正P/Invoke函数声明

WTSEnumerateSessionsExA的参数声明有几个关键错误:

  • ppSessionInfo:原生返回指向结构体数组的指针,不能直接用out WtsSessionInfoClass[],应该用out IntPtr,之后手动Marshal为结构体数组
  • pCount:这是输入输出参数,需要用ref UIntPtr,函数会通过它返回会话数量
  • 添加CallingConvention = CallingConvention.StdCall(WtsApi32函数使用stdcall调用约定)
  • 补充WTSFreeMemoryEx函数声明,用来释放函数返回的内存,避免泄漏

修正后的Native32类:

internal static class Native32
{
    [DllImport("WtsApi32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    internal static extern IntPtr WTSOpenServerEx(string name);

    [DllImport("WtsApi32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool WTSEnumerateSessionsExA(
        IntPtr hServer,
        ref UIntPtr pLevel,
        uint Filter,
        out IntPtr ppSessionInfo,
        ref UIntPtr pCount
    );

    [DllImport("WtsApi32.dll", CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool WTSFreeMemoryEx(
        WTS_MEMORY_TYPE MemoryType,
        IntPtr pMemory,
        UIntPtr NumberOfEntries
    );

    [DllImport("WtsApi32.dll", CallingConvention = CallingConvention.StdCall)]
    internal static extern void WTSCloseServer(IntPtr hServer);

    internal enum WTS_MEMORY_TYPE
    {
        WTSMemTypeSessionInfoLevel1 = 1
    }
}

4. 正确的调用流程

现在可以安全调用函数,注意要处理内存释放和句柄关闭:

IntPtr serverHandle = Native32.WTSOpenServerEx("MyMachineName");
if (serverHandle == IntPtr.Zero)
{
    // 处理服务器打开失败的情况
    return;
}

UIntPtr sessionCount = UIntPtr.Zero;
IntPtr sessionInfoPtr = IntPtr.Zero;

try
{
    UIntPtr level = new UIntPtr(1); // 指定信息级别为1,对应WTS_SESSION_INFO_1A
    bool success = Native32.WTSEnumerateSessionsExA(
        serverHandle,
        ref level,
        0, // 0表示枚举所有会话
        out sessionInfoPtr,
        ref sessionCount
    );

    if (!success || sessionCount == UIntPtr.Zero || sessionInfoPtr == IntPtr.Zero)
    {
        // 处理枚举失败,可通过Marshal.GetLastWin32Error()获取错误码
        return;
    }

    // 将指针转换为结构体数组
    WTS_SESSION_INFO_1A[] sessions = new WTS_SESSION_INFO_1A[(int)sessionCount];
    int structSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO_1A));
    for (int i = 0; i < (int)sessionCount; i++)
    {
        IntPtr currentPtr = IntPtr.Add(sessionInfoPtr, i * structSize);
        sessions[i] = Marshal.PtrToStructure<WTS_SESSION_INFO_1A>(currentPtr);

        // 转换字符串成员
        string sessionName = Marshal.PtrToStringAnsi(sessions[i].pSessionName);
        string hostName = Marshal.PtrToStringAnsi(sessions[i].pHostName);
        string userName = Marshal.PtrToStringAnsi(sessions[i].pUserName);
        // 其他字符串成员同理处理
    }

    // 这里可以使用sessions数组做后续业务逻辑
}
finally
{
    // 释放会话信息内存
    if (sessionInfoPtr != IntPtr.Zero)
    {
        Native32.WTSFreeMemoryEx(Native32.WTS_MEMORY_TYPE.WTSMemTypeSessionInfoLevel1, sessionInfoPtr, sessionCount);
    }
    // 关闭服务器句柄
    Native32.WTSCloseServer(serverHandle);
}

这些修正应该能解决你遇到的AccessViolationException问题,核心是确保C#的结构体和函数声明完全匹配原生API的定义,同时正确处理非托管内存的分配和释放。

内容的提问来源于stack exchange,提问作者PauMAVA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 00:32:45