问题描述
我正在尝试使用WtsEnumerateSessionsExA
中定义的功能WtsApi32.dll
枚举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),out sessions,len); // This function will produce AccessViolationException
...
执行代码时,由于Native32.WTSEnumerateSessionsExA
,对AccessViolationException
的调用失败。
我在这里做错了什么?任何帮助表示赞赏。
解决方法
我最好的猜测是您没有正确地传递第二个参数pLevel
。您正试图传递一个指向值为1的DWORD的指针,但实际上传递的是地址1。相反,声明一个初始化为1的局部变量,然后传递该地址。
更多详细信息:
您链接的WTSEnumerateSessionsExA的文档页面上说:
pLevel
此参数保留。始终将此参数设置为一个。在输出时, WTSEnumerateSessionsEx 不会更改此参数的值。
这里的措辞不是最好的,但是我的解释是您需要传递pLevel
的有效指针,并且该指针需要指向值为1的正确对齐的DWORD。
对我来说,另一种读法是您需要传递文字存储地址1作为pLevel
的指针。我本能地觉得这很荒谬。
但是,您的代码:
Native32.WTSEnumerateSessionsExA(
handle,new UIntPtr(1),out sessions,len);
传入new UIntPtr(1)
的{{1}}。根据我对UIntPtr doc pages的阅读,该表达式将创建一个指针,其字面值为1,即地址1。
如果您调试了WTSEnumerateSessionsEx实现的汇编代码,我的猜测是您会看到它尝试取消对pLevel
的引用,然后崩溃,并在尝试从地址加载DWORD时遇到访问冲突1。
要解决此问题,您可以声明一个包含值1的pLevel
变量,并将地址传递给该变量:
uint