尝试枚举终端服务器会话会产生 AccessViolationException(.NET 和本机 DLL)
Trying to enumerate Terminal Server sessions produces AccessViolationException (.NET and Native DLL)
我正在尝试使用 WtsApi32.dll
中定义的函数 WtsEnumerateSessionsExA
在 C# 中枚举所有远程桌面会话。
此函数写入一个 WTS_SESSION_INFO_1A
结构数组,其指针作为参数传递。您可以在此处找到文档:
我在 C# 中实现了 WTS_SESSION_INFO_1A
结构和 WTS_CONNECT_STATE_CLASS
,如下所示:
struct WtsSessionInfoClass
{
uint ExecEnvId;
WtsConnectStateClass state;
uint sessionId;
string pSessionName;
string pHostName;
string pUserName;
string pDomainName;
string pFarmName;
}
enum WtsConnectStateClass
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
我已经在名为 Native32
:
的 class 中导入了所需的函数
[DllImport("WtsApi32.dll")]
internal static extern IntPtr WTSOpenServerEx(string name);
[DllImport("WtsApi32.dll")]
internal static extern bool WTSEnumerateSessionsExA(
IntPtr hServer,
UIntPtr pLevel,
uint Filter,
out WtsSessionInfoClass[] ppSessionInfo,
UIntPtr pCount
);
最后我的实现如下:
UIntPtr len = new UIntPtr();
WtsSessionInfoClass[] sessions;
IntPtr handle = Native32.WTSOpenServerEx("MyMachineName"); // This function gets the server handle and works fine
Native32.WTSEnumerateSessionsExA(handle, new UIntPtr(1), 0, out sessions, len); // This function will produce AccessViolationException
...
执行代码时,调用Native32.WTSEnumerateSessionsExA
失败,原因是AccessViolationException
。
我在这里做错了什么?感谢任何帮助。
我最好的猜测是您没有以正确的方式传递第二个参数 pLevel
。您试图传递一个指向值为 1 的 DWORD 的指针,但您实际传递的是地址 1。相反,声明一个初始化为 1 的局部变量并传递它的地址。
更多详情:
您链接的 WTSEnumerateSessionsExA 的文档页面是这样说的:
pLevel
This parameter is reserved. Always set this parameter to one. On output, WTSEnumerateSessionsEx does not change the value of this parameter.
这里的措辞不是最好的,但我的解释是你需要为 pLevel
传递一个有效的指针,并且该指针需要指向一个正确对齐的值为 1 的 DWORD。
对我来说,另一种解读是您需要传递文字内存地址 1 作为 pLevel
的指针。我本能地觉得这很荒谬。
但是,您的代码:
Native32.WTSEnumerateSessionsExA(
handle, new UIntPtr(1), 0, out sessions, len);
通过 new UIntPtr(1)
获得 pLevel
。根据我对 UIntPtr doc pages 的阅读,该表达式将创建一个字面值为 1 的指针,换句话说,地址 1.
如果您调试 WTSEnumerateSessionsEx 实现的汇编代码,我猜您会看到它尝试取消引用 pLevel
,然后由于您在尝试加载 DWORD 时看到的访问冲突而崩溃来自地址 1.
要解决此问题,您可以声明一个包含值 1 的 uint
变量,并将地址传递给该变量:
uint enumerateSessionsLevel = 1;
Native32.WTSEnumerateSessionsExA(
handle, &enumerateSessionsLevel, 0, out sessions, len);
我正在尝试使用 WtsApi32.dll
中定义的函数 WtsEnumerateSessionsExA
在 C# 中枚举所有远程桌面会话。
此函数写入一个 WTS_SESSION_INFO_1A
结构数组,其指针作为参数传递。您可以在此处找到文档:
我在 C# 中实现了 WTS_SESSION_INFO_1A
结构和 WTS_CONNECT_STATE_CLASS
,如下所示:
struct WtsSessionInfoClass
{
uint ExecEnvId;
WtsConnectStateClass state;
uint sessionId;
string pSessionName;
string pHostName;
string pUserName;
string pDomainName;
string pFarmName;
}
enum WtsConnectStateClass
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
我已经在名为 Native32
:
[DllImport("WtsApi32.dll")]
internal static extern IntPtr WTSOpenServerEx(string name);
[DllImport("WtsApi32.dll")]
internal static extern bool WTSEnumerateSessionsExA(
IntPtr hServer,
UIntPtr pLevel,
uint Filter,
out WtsSessionInfoClass[] ppSessionInfo,
UIntPtr pCount
);
最后我的实现如下:
UIntPtr len = new UIntPtr();
WtsSessionInfoClass[] sessions;
IntPtr handle = Native32.WTSOpenServerEx("MyMachineName"); // This function gets the server handle and works fine
Native32.WTSEnumerateSessionsExA(handle, new UIntPtr(1), 0, out sessions, len); // This function will produce AccessViolationException
...
执行代码时,调用Native32.WTSEnumerateSessionsExA
失败,原因是AccessViolationException
。
我在这里做错了什么?感谢任何帮助。
我最好的猜测是您没有以正确的方式传递第二个参数 pLevel
。您试图传递一个指向值为 1 的 DWORD 的指针,但您实际传递的是地址 1。相反,声明一个初始化为 1 的局部变量并传递它的地址。
更多详情:
您链接的 WTSEnumerateSessionsExA 的文档页面是这样说的:
pLevel
This parameter is reserved. Always set this parameter to one. On output, WTSEnumerateSessionsEx does not change the value of this parameter.
这里的措辞不是最好的,但我的解释是你需要为 pLevel
传递一个有效的指针,并且该指针需要指向一个正确对齐的值为 1 的 DWORD。
对我来说,另一种解读是您需要传递文字内存地址 1 作为 pLevel
的指针。我本能地觉得这很荒谬。
但是,您的代码:
Native32.WTSEnumerateSessionsExA(
handle, new UIntPtr(1), 0, out sessions, len);
通过 new UIntPtr(1)
获得 pLevel
。根据我对 UIntPtr doc pages 的阅读,该表达式将创建一个字面值为 1 的指针,换句话说,地址 1.
如果您调试 WTSEnumerateSessionsEx 实现的汇编代码,我猜您会看到它尝试取消引用 pLevel
,然后由于您在尝试加载 DWORD 时看到的访问冲突而崩溃来自地址 1.
要解决此问题,您可以声明一个包含值 1 的 uint
变量,并将地址传递给该变量:
uint enumerateSessionsLevel = 1;
Native32.WTSEnumerateSessionsExA(
handle, &enumerateSessionsLevel, 0, out sessions, len);