13

C# で記述された Windows サービスから、ローカル Windows セッションでユーザーをログオフする方法を見つけようとしています。

問題の背景は次のとおりです。一連のユーザーのコンピューター使用時間を管理する必要があります。割り当てられた時間が切れたら、ログオフしたいと思います。これは、W2K8 ドメインのコンテキストにあります。残念ながら、Windows のログイン時間制御は、ユーザーをサーバー リソースから切断するだけです。この方法でセッションを強制的に終了させる方法はありません。

私のアプローチは、ドメイン全体に展開する Windows サービスを構築することです。サービスはすべてのクライアント コンピューターで実行されます。このサービスは定期的に、コンピューターにログインしているユーザーを列挙し、データベースを呼び出して、最後の呼び出し以降のログイン時間を 1 日の合計に追加し、ユーザーが最大数に達した場合はログアウトします ( 5 分間の警告があります)。注 - これらはターミナル サービス セッションではなく、通常のローカル インタラクティブ ログオンです。また、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;

ただし、選択したユーザーのセッションをログオフできる方法を見つけるのに苦労しています。マシンをシャットダウンできることはわかっていますが、それはしたくありません。すべてのユーザーのセッションが強制終了されます。

アイデアはありますか?この長い投稿を読んでくれてありがとう!

4

1 に答える 1

20

これを実現するには、次の 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);

すべてのユーザーとそのセッションを検索し、ユーザーの 1 人をログオフする実装例を次に示します。

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);
        }
    }
}
}
于 2011-03-06T09:00:25.213 に答える