我必须开发一个程序,它在本地PC上作为服务运行,为服务器提供几个用户状态 . 一开始我必须检测用户 logon 和 logoff .
我的想法是使用 ManagementEventWatcher
类并查询 Win32_LogonSession
如果发生了变化则会收到通知 .
我的第一个测试运行良好,这是代码部分(这将作为服务的线程执行):
private readonly static WqlEventQuery qLgi = new WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 1), "TargetInstance ISA \"Win32_LogonSession\"");
public EventWatcherUser() {
}
public void DoWork() {
ManagementEventWatcher eLgiWatcher = new ManagementEventWatcher(EventWatcherUser.qLgi);
eLgiWatcher.EventArrived += new EventArrivedEventHandler(HandleEvent);
eLgiWatcher.Start();
}
private void HandleEvent(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject f = (ManagementBaseObject)e.NewEvent["TargetInstance"];
using (StreamWriter fs = new StreamWriter("C:\\status.log", true))
{
fs.WriteLine(f.Properties["LogonId"].Value);
}
}
但我有一些理解问题,我不确定这是否是解决该任务的常用方法 .
- 如果我查询
Win32_LogonSession
,我会得到几条与同一用户相关联的记录 . 例如,我得到这个ID 7580798和7580829,如果我查询
ASSOCIATORS OF WHERE ResultClass = Win32_UserAccount
我获得了不同ID的相同记录 . (Win32_UserAccount.Domain = “PC-名称”,名称= “用户1”)
为什么有多个与同一用户的登录会话?获取当前用户签名的常用方法是什么?或者更好的方法是如何通过用户登录正确收到通知?
- 我以为我可以用
__InstanceDeletionEvent
以相同的方式确定用户是否注销 . 但我想如果事件被提出,那么在此之后我无法查询Win32_UserAccount
的用户名 . 我是正确的?
我是在正确的方向还是有更好的方法?如果你可以帮助我,那真是太棒了!
Edit WTSRegisterSessionNotification类是正确的方法吗?我没有't know if it'可能,因为在服务中我没有窗口处理程序 .
4 回答
由于您使用的是服务,因此可以直接获取会话更改事件 .
您可以注册自己以接收
SERVICE_CONTROL_SESSIONCHANGE
事件 . 特别是,您需要查找WTS_SESSION_LOGON
和WTS_SESSION_LOGOFF
的原因 .有关相关MSDN文档的详细信息和链接,请查看this answer I wrote just yesterday .
在C#中它更容易,因为ServiceBase已经包装了服务控制例程并将事件公开为可覆盖的
OnSessionChange
方法 . 请参阅MSDN docs for ServiceBase,并且不要忘记将CanHandleSessionChangeEvent
属性设置为true以启用此方法的执行 .当框架调用
OnSessionChange
覆盖时,你得到的是SessionChangeDescription Structure,其原因(注销,登录,...)和会话ID可用于获取信息,例如,用户登录/注销(请参阅链接到我的热门答案了解详情)编辑:示例代码
您可以使用属于Windows的System Event Notification Service技术 . 它具有ISensLogon2 interface,它提供登录/注销事件(以及其他事件,如远程会话连接) .
这是一段代码(示例控制台应用程序),演示了如何执行此操作 . 您可以使用来自另一台计算机的远程桌面会话对其进行测试,例如,这将触发SessionDisconnect,SessionReconnect事件 .
此代码应支持从XP到Windows 8的所有Windows版本 .
Note 请务必将嵌入互操作类型设置为'False',否则会出现以下错误:"Interop type 'COMAdminCatalogClass' cannot be embedded. Use the applicable interface instead."
与互联网上有关在.NET中使用此技术的其他文章相反,它不引用Sens.dll,因为它在Windows 8上似乎不存在(我不知道为什么) . 然而,该技术似乎得到支持,并且SENS服务确实安装并在Windows 8上正常运行,因此您只需手动声明接口和guids(如本示例中所示),或引用在早期版本的Windows上创建的互操作程序集(它应该工作正常,因为guids和各种接口没有改变) .
Note: 通过右键单击Visual Studio快捷方式并单击
run as administrator
,确保Visual Studio以管理员权限运行,否则在程序运行时将抛出System.UnauthorizedAccessException
.这是代码(所有代码都驻留在类中;在我的例子中,类继承
ServiceBase
) . 如果您还想获取登录用户的用户名,这将特别有用 .使用您的类中的上述代码,您可以简单地获取您要覆盖的方法中的用户名,如下所示:
注意:记得在继承自
ServiceBase
的类的构造函数中添加CanHandleSessionChangeEvent = true;
我使用ServiceBase.OnSessionChange来捕获不同的用户事件并在之后加载必要的信息 .
要加载会话信息,我使用WTS_INFO_CLASS . 请参阅下面的示例:
下列代码使用来自
User
的静态AvailabilityChanged
事件,一旦会话状态发生变化就会被触发 . arge
包含特定用户 .