2011-03-05 50 views
11

我想弄清楚用C#編寫的Windows服務註銷本地Windows會話中的用戶的方式。從服務中註銷Windows中的交互式用戶

下面是問題的背景: 我需要管理一組用戶的計算機使用時間;當他們的分配時間到期時,我想將他們註銷。這是在W2K8域的上下文中。不幸的是,Windows中的登錄時間控制只是將用戶從服務器資源中斷開;沒有辦法強制他們的會話通過這種方法終止。

我的方法是構建一個Windows服務,我將跨域部署;該服務將在每臺客戶端計算機上運行。服務會定期在計算機上枚舉登錄用戶,呼叫數據庫以將自上次調用以來的登錄時間添加到當天的總時間,並且如果他們已達到其最大值,則將其註銷(五分鐘警告)。請注意 - 這些不是終端服務會話,它們是常規的本地交互式登錄。另請注意,由於Win7 & Vista中的「切換用戶」功能,機器上可能會有多個登錄。我所有的客戶端電腦都將運行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; 

不過,我苦苦尋找,讓我註銷選定的用戶會話的方法。我知道我可以關閉機器,但我不想那樣 - 這會殺死所有用戶的會話。

任何想法的人?感謝您閱讀這篇冗長的文章!

+0

聽起來像是你可能需要得到阿霍德,殺,每個用戶的窗口站:http://msdn.microsoft.com/en-us/library/ms681928(v=VS.85).aspx - 你可能需要通過Win32調用來完成。儘管這很麻煩,但是你可以從.NET中做到這一點。 – 2011-03-05 23:40:25

+0

您可以使用WMI註銷活動控制檯會話(通過Win32Shutdown),但我認爲它不適用於多個用戶。 – Neil 2011-03-06 00:05:53

+0

@Ed - 感謝這個建議,我從跟進中瞭解了Window Stations。從我可以看到,雖然(我可能是錯的),它看起來像只有在用戶的會話中執行才能工作。我不認爲我可以從另一場會議中殺死他們的窗口站。 – multiSingularity 2011-03-06 12:04:45

回答

15

您可以使用以下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<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); 
     } 
    } 
} 
} 
+0

非常感謝您的全面回覆。這看起來有希望。我會試一試,看看會發生什麼。 – multiSingularity 2011-03-06 12:10:33

+1

@multiSingularity,這種技術有用嗎?如果沒有評論你的問題或編輯問題,以便任何人都可以看看它並解決。 – 2011-03-12 00:06:12

+3

很抱歉,長時間的耽擱回到你身邊......這是一個個人項目,工作一直很荒唐......直到現在還沒有機會回到這個地步! 您的解決方案非常完美 - 非常感謝您。 – multiSingularity 2011-05-14 05:15:17