从服务注销Windows中的交互式用户

我正试图找出一种方法来从使用C#编写的Windows服务注销本地Windows会话中的用户。

以下是问题的背景:我需要管理一组用户的计算机使用时间; 当他们的分配时间到期时,我想将它们记录下来。 这是在W2K8域的上下文中。 不幸的是,Windows中的登录时间控件只是将用户与服务器资源断开连接; 没有办法强制他们的会话通过这种方法终止。

我的方法是构建一个我将在域中部署的Windows服务; 该服务将在每台客户端计算机上运行。 服务将定期枚举计算机上的已登录用户,呼叫数据库以将自上次呼叫以来的登录时间添加到当天的总计中,如果已达到最大值,则将其注销(五分钟警告)。 注意 – 这些不是终端服务会话,它们是常规的本地交互式登录。 另请注意,由于Win7和Vista中的“切换用户”function,计算机上可能会有多个登录。 我的所有客户端PC都将运行Win7。 Windows服务将作为本地系统运行,因此权限不应成为问题。

我可以使用WMI通过用户名在计算机上成功构建登录用户列表。 这是该代码的片段:


List loggedInUsers = new List(); ManagementClass mc = new ManagementClass("Win32_Process"); ManagementObjectCollection moc = mc.GetInstances(); foreach (ManagementObject mo in moc) { ROOT.CIMV2.Process process = new ROOT.CIMV2.Process(mo); string domain, user; uint pid; process.GetOwner(out domain, out user); pid = process.ProcessId; if (process.Name.Trim().ToLower() == "explorer.exe") loggedInUsers.Add(user); } return loggedInUsers; 

但是,我正在努力寻找一种方法,允许我注销所选用户的会话。 我知道我可以关闭机器,但我不希望这样 – 这会杀死所有用户的会话。

任何人的想法? 感谢您阅读这篇冗长的post!

您可以使用以下P / Invoke调用来实现此目的。 以下示例仅适用于管理员权限

  [DllImport("wtsapi32.dll", SetLastError = true)] static extern bool WTSLogoffSession(IntPtr hServer, int SessionId, bool bWait); [DllImport("Wtsapi32.dll")] static extern bool WTSQuerySessionInformation( System.IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned); [DllImport("wtsapi32.dll", SetLastError = true)] static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName); [DllImport("wtsapi32.dll")] static extern void WTSCloseServer(IntPtr hServer); [DllImport("wtsapi32.dll", SetLastError = true)] static extern Int32 WTSEnumerateSessions(IntPtr hServer, [MarshalAs(UnmanagedType.U4)] Int32 Reserved, [MarshalAs(UnmanagedType.U4)] Int32 Version, ref IntPtr ppSessionInfo, [MarshalAs(UnmanagedType.U4)] ref Int32 pCount); [DllImport("wtsapi32.dll")] static extern void WTSFreeMemory(IntPtr pMemory); 

下面是一个示例实现,用于查找所有用户及其会话,然后注销其中一个用户。

 using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace ConsoleApplication1 { [StructLayout(LayoutKind.Sequential)] internal struct WTS_SESSION_INFO { public Int32 SessionID; [MarshalAs(UnmanagedType.LPStr)] public String pWinStationName; public WTS_CONNECTSTATE_CLASS State; } internal enum WTS_CONNECTSTATE_CLASS { WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit } internal enum WTS_INFO_CLASS { WTSInitialProgram, WTSApplicationName, WTSWorkingDirectory, WTSOEMId, WTSSessionId, WTSUserName, WTSWinStationName, WTSDomainName, WTSConnectState, WTSClientBuildNumber, WTSClientName, WTSClientDirectory, WTSClientProductId, WTSClientHardwareId, WTSClientAddress, WTSClientDisplay, WTSClientProtocolType, WTSIdleTime, WTSLogonTime, WTSIncomingBytes, WTSOutgoingBytes, WTSIncomingFrames, WTSOutgoingFrames, WTSClientInfo, WTSSessionInfo } class Program { [DllImport("wtsapi32.dll", SetLastError = true)] static extern bool WTSLogoffSession(IntPtr hServer, int SessionId, bool bWait); [DllImport("Wtsapi32.dll")] static extern bool WTSQuerySessionInformation( System.IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned); [DllImport("wtsapi32.dll", SetLastError = true)] static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName); [DllImport("wtsapi32.dll")] static extern void WTSCloseServer(IntPtr hServer); [DllImport("wtsapi32.dll", SetLastError = true)] static extern Int32 WTSEnumerateSessions(IntPtr hServer, [MarshalAs(UnmanagedType.U4)] Int32 Reserved, [MarshalAs(UnmanagedType.U4)] Int32 Version, ref IntPtr ppSessionInfo, [MarshalAs(UnmanagedType.U4)] ref Int32 pCount); [DllImport("wtsapi32.dll")] static extern void WTSFreeMemory(IntPtr pMemory); internal static List GetSessionIDs(IntPtr server) { List sessionIds = new List(); IntPtr buffer = IntPtr.Zero; int count = 0; int retval = WTSEnumerateSessions(server, 0, 1, ref buffer, ref count); int dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO)); Int64 current = (int)buffer; if (retval != 0) { for (int i = 0; i < count; i++) { WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO)); current += dataSize; sessionIds.Add(si.SessionID); } WTSFreeMemory(buffer); } return sessionIds; } internal static bool LogOffUser(string userName, IntPtr server) { userName = userName.Trim().ToUpper(); List sessions = GetSessionIDs(server); Dictionary userSessionDictionary = GetUserSessionDictionary(server, sessions); if (userSessionDictionary.ContainsKey(userName)) return WTSLogoffSession(server, userSessionDictionary[userName], true); else return false; } private static Dictionary GetUserSessionDictionary(IntPtr server, List sessions) { Dictionary userSession = new Dictionary(); foreach (var sessionId in sessions) { string uName = GetUserName(sessionId, server); if (!string.IsNullOrWhiteSpace(uName)) userSession.Add(uName, sessionId); } return userSession; } internal static string GetUserName(int sessionId, IntPtr server) { IntPtr buffer = IntPtr.Zero; uint count = 0; string userName = string.Empty; try { WTSQuerySessionInformation(server, sessionId, WTS_INFO_CLASS.WTSUserName, out buffer, out count); userName = Marshal.PtrToStringAnsi(buffer).ToUpper().Trim(); } finally { WTSFreeMemory(buffer); } return userName; } static void Main(string[] args) { string input = string.Empty; Console.Write("Enter ServerName:"); input = Console.ReadLine(); IntPtr server = WTSOpenServer(input.Trim()[0] == '0' ? Environment.MachineName : input.Trim()); try { do { Console.WriteLine("Please Enter L => list sessions, G => Logoff a user, END => exit."); input = Console.ReadLine(); if (string.IsNullOrWhiteSpace(input)) continue; else if (input.ToUpper().Trim()[0] == 'L') { Dictionary userSessionDict = GetUserSessionDictionary(server, GetSessionIDs(server)); foreach (var userSession in userSessionDict) { Console.WriteLine(string.Format("{0} is logged in {1} session", userSession.Key, userSession.Value)); } } else if (input.ToUpper().Trim()[0] == 'G') { Console.Write("Enter UserName:"); input = Console.ReadLine(); LogOffUser(input, server); } } while (input.ToUpper() != "END"); } finally { WTSCloseServer(server); } } } }