【问题标题】:How to determine the current windows user from a windows service?如何从 Windows 服务中确定当前的 Windows 用户?
【发布时间】:2010-01-17 08:13:04
【问题描述】:

我正在使用 C# .NET 2005 编写 Windows 服务。如何确定当前登录的用户是谁(如果有)?还有没有办法在用户登录时得到通知?

或者,有没有办法知道谁最近使用过这台机器?

我需要知道当前登录的用户,以便为该用户缓存一些数据。在公司环境中运营有成千上万的潜在用户,但只有为使用该机器的人缓存数据才有意义。

更新:

This solution 运行良好。另请参阅this pinvoke.net example,它使用扩展结构来检索域名。

结合这一点,我使用 SystemEvents 类在用户登录机器时收到通知。请参阅 example 2 here 获取一个很好的示例 - 请注意,您需要使用服务中的隐藏表单才能使用服务中的 SystemEvents。

【问题讨论】:

    标签: c# .net windows-services


    【解决方案1】:

    您可以使用 P/Invoke 调用NetWkstaUserEnum,它将枚举当前登录的用户。请记住,如果存在终端服务器会话,则可能有多个用户,并且并非所有返回的用户都是“真实”用户。正如文档所述:

    “此列表包括交互式、 服务和批量登录。”

    这是一个完整的 C# 代码示例,说明如何调用 NetWkstaUserEnum:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    
    namespace EnumerateUsers
    {
        class Program
        {
            static void Main(string[] args)
            {
                var ue = new UserEnumerator();
                foreach(string userName in ue.GetLoggedOnUsers(null))
                {
                    Console.WriteLine(userName);
                }
            }
    
        }
    
        class UserEnumerator
        {
            public IEnumerable<string> GetLoggedOnUsers(string host)
            {
                int entriesRead, totalEntries, resumeHandle = 0;
                IntPtr pBuffer = IntPtr.Zero;
                try
                {
                    int result = NetWkstaUserEnum(host, 0, out pBuffer, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, ref resumeHandle);
                    if (result != NERR_Success)  
                        throw new ApplicationException(String.Format("Failed to enumerate users, error code {0}", result));
    
                    return GetUsersFromStruct(pBuffer, entriesRead).ToList();
                }
                finally
                {
                    if (pBuffer != IntPtr.Zero)
                        NetApiBufferFree(pBuffer);
                    }
    
            }
    
            private IEnumerable<string> GetUsersFromStruct(IntPtr pBuffer, int count)
            {
                for (int i = 0; i < count; i++)
                {
                    var user = (WKSTA_USER_INFO_0)Marshal.PtrToStructure(pBuffer, typeof(WKSTA_USER_INFO_0));
                    yield return user.username;
                    pBuffer = IntPtr.Add(pBuffer, user.username.Length * 2);                
                }
            }
            [DllImport("netapi32.dll")]
            private static extern int NetWkstaUserEnum(string host, int level, out IntPtr pBuffer, int prefMaxLength, out int entriesRead,
                                         out int totalEntries, ref int resumeHandle);
    
            [DllImport("netapi32.dll")]
            private static extern int NetApiBufferFree(IntPtr buffer);
    
            private const int MAX_PREFERRED_LENGTH = -1;
    
            private const int NERR_Success = 0;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        struct WKSTA_USER_INFO_0
        { 
            [MarshalAs(UnmanagedType.LPTStr)]
            internal string username;
        }
    }
    

    【讨论】:

    • 谢谢,太好了。我注意到它可以返回同一个用户两次,所以如果需要唯一值,我会添加一个重复数据删除步骤。
    【解决方案2】:

    如您所知,Windows 服务中可能没有当前登录的用户。为什么不在机器上的启动登录过程中添加一个小实用程序,它会在有人登录时运行,它会执行一个调用服务中数据缓存功能的方法。这样,该实用程序就可以访问登录用户的windows原则身份。

    【讨论】:

    • 虽然这可能更容易构建,但这意味着解决方案有更多的部署和运行时依赖性,因此会出现更多问题:例如,如果有人更改启动登录过程以禁用该实用程序,该怎么办。如果有标准的程序化解决方案会更好。
    • @Rory,我猜这是一个服务器,这样的问题不是什么问题......即,除了极少数情况外,唯一登录的人是使用远程桌面远程登录.. 此外,通过将功能置于登录过程中,意味着您不必处理多个并发登录。但是这两种方法都行得通……
    猜你喜欢
    • 2023-03-30
    • 2014-03-07
    • 2021-09-05
    • 1970-01-01
    • 2018-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多