首页 文章

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

提问于
浏览
12

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

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

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

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


List<string> loggedInUsers = new List<string>();
        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;

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

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

1 回答

  • 16

    您可以使用以下P / Invoke调用来实现此目的 . Below sample works only with Admin Rights

    [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<int> GetSessionIDs(IntPtr server)
        {
            List<int> sessionIds = new List<int>();
            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<int> sessions = GetSessionIDs(server);
            Dictionary<string, int> userSessionDictionary = GetUserSessionDictionary(server, sessions);
            if (userSessionDictionary.ContainsKey(userName))
                return WTSLogoffSession(server, userSessionDictionary[userName], true);
            else
                return false;
        }
    
        private static Dictionary<string, int> GetUserSessionDictionary(IntPtr server, List<int> sessions)
        {
            Dictionary<string, int> userSession = new Dictionary<string, int>();
    
            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<Enter 0 to default to local>:");
            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<string, int> 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);
            }
        }
    }
    }
    

相关问题