尝试枚举终端服务器会话会产生AccessViolationException(.NET和本机DLL)

尝试枚举终端服务器会话会产生AccessViolationException(.NET和本机DLL),.net,pinvoke,dllimport,wtsapi32,.net,Pinvoke,Dllimport,Wtsapi32,我正在尝试使用WtsApi32.dll中定义的函数WtsEnumerateSessionsExA枚举C中的所有远程桌面会话 此函数将指针作为参数传递到WTS\u SESSION\u INFO\u 1A结构的数组中。您可以在此处找到文档: 我在C语言中实现了WTS\u SESSION\u INFO\u 1Astruct和WTS\u CONNECT\u STATE\u类,如下所示: struct-WtsSessionInfoClass { 不可执行; WtsConnectStateClas

我正在尝试使用
WtsApi32.dll
中定义的函数
WtsEnumerateSessionsExA
枚举C中的所有远程桌面会话

此函数将指针作为参数传递到
WTS\u SESSION\u INFO\u 1A
结构的数组中。您可以在此处找到文档:

我在C语言中实现了
WTS\u SESSION\u INFO\u 1A
struct和
WTS\u CONNECT\u STATE\u类
,如下所示:

struct-WtsSessionInfoClass
{
不可执行;
WtsConnectStateClass状态;
uint sessionId;
字符串pSessionName;
线状磷铵;
字符串pUserName;
字符串名称;
字符串名称;
}
我已经在名为
Native32
的类中导入了所需的函数:

[DllImport(“WtsApi32.dll”)]
内部静态外部IntPtr wtsopenservex(字符串名称);
[DllImport(“WtsApi32.dll”)]
内部静态外部布尔WTSEnumerateSessionExa(
IntPtr服务器,
UIntPtr pLevel,
uint过滤器,
输出WtsSessionInfoClass[]PPSSessionInfo,
UIntPtr pCount
);
最后,我的实现如下:

UIntPtr len=new uintpttr();
WtsSessionInfoClass[]课程;
IntPtr handle=Native32.wtsopenservex(“MyMachineName”);//此函数获取服务器句柄,工作正常
Native32.WTSEnumerateSessionsExA(句柄,新uintpttr(1),0,out sessions,len);//此函数将生成AccessViolationException
...
执行代码时,对
Native32.WTSEnumerateSessionsExA
的调用由于
AccessViolationException
而失败


我做错了什么?非常感谢您的帮助。

我的猜测是,您没有以正确的方式传递第二个参数
pLevel
。您试图传递一个指向值为1的DWORD的指针,但实际传递的是地址1。相反,声明一个初始化为1的局部变量,并传递该变量的地址

更多详细信息:

您链接的WTSEnumerateSessionsExA的文档页面显示:

pLevel

此参数是保留的。始终将此参数设置为1。在输出时,WTSEnumerateSessionEx不会更改此参数的值

这里的措辞不是最好的,但我的解释是,您需要为
pLevel
传递一个有效指针,该指针需要指向一个正确对齐的DWORD,其值为1

对我来说,另一种读取方式是,您需要传递文本内存地址1作为
pLevel
的指针。我本能地觉得这很荒谬

但是,您的代码:

Native32.WTSEnumerateSessionsExA(
句柄,新uintpttr(1),0,输出会话,len);
pLevel
传入新的UIntPtr(1)。根据我对的理解,该表达式将创建一个指针,其文本值为1,换句话说,地址为1

如果您调试到WTSEnumerateSessionEx实现的汇编代码中,我猜您会看到它尝试取消引用
pLevel
,然后在尝试从地址1加载DWORD时遇到访问冲突而崩溃

要解决此问题,可以声明一个包含值1的
uint
变量,并将地址传递给该变量:

uint EnumerateSessionLevel=1;
Native32.WTSEnumerateSessionsExA(
handle,&枚举会话级别,0,out会话,len);

我的最佳猜测是,您没有以正确的方式传递第二个参数
pLevel
。您试图传递一个指向值为1的DWORD的指针,但实际传递的是地址1。相反,声明一个初始化为1的局部变量,并传递该变量的地址

更多详细信息:

您链接的WTSEnumerateSessionsExA的文档页面显示:

pLevel

此参数是保留的。始终将此参数设置为1。在输出时,WTSEnumerateSessionEx不会更改此参数的值

这里的措辞不是最好的,但我的解释是,您需要为
pLevel
传递一个有效指针,该指针需要指向一个正确对齐的DWORD,其值为1

对我来说,另一种读取方式是,您需要传递文本内存地址1作为
pLevel
的指针。我本能地觉得这很荒谬

但是,您的代码:

Native32.WTSEnumerateSessionsExA(
句柄,新uintpttr(1),0,输出会话,len);
pLevel
传入新的UIntPtr(1)。根据我对的理解,该表达式将创建一个指针,其文本值为1,换句话说,地址为1

如果您调试到WTSEnumerateSessionEx实现的汇编代码中,我猜您会看到它尝试取消引用
pLevel
,然后在尝试从地址1加载DWORD时遇到访问冲突而崩溃

要解决此问题,可以声明一个包含值1的
uint
变量,并将地址传递给该变量:

uint EnumerateSessionLevel=1;
Native32.WTSEnumerateSessionsExA(
handle,&枚举会话级别,0,out会话,len);

非常感谢您!我还必须为
len
创建另一个
uint
变量,并将其作为指针
&len
传递。不要使用
new uintpttr()
。非常感谢!我还必须为
len
创建另一个
uint
变量,并将其作为指针
&len
传递。而不是使用新的UIntPtr()。
enum WtsConnectStateClass
{
    WTSActive,
    WTSConnected,
    WTSConnectQuery,
    WTSShadow,
    WTSDisconnected,
    WTSIdle,
    WTSListen,
    WTSReset,
    WTSDown,
    WTSInit
}