2010-01-16 28 views
1

我正在使用C#.NET 2005编写Windows服务。我如何确定当前登录的用户是谁(如果有的话)?当用户登录时还有一种通知方式吗?如何从Windows服务中确定当前的Windows用户?

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

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

UPDATE:

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

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

回答

3

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

谢谢,这太棒了。我注意到它可以返回两次相同的用户,所以如果需要唯一值,我会添加一个去重复步骤。 – Rory

0

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

+0

尽管构建起来可能更简单,但这意味着解决方案会有更多的部署和运行时依赖关系,因此会出现更多问题:例如,如果某人更改了启动登录进程以禁用实用程序,该怎么办?如果有一个标准的程序化解决方案会更好。 – Rory

+0

@Rory,我猜这是一个服务器,在这种情况下问题不大......除了极少数情况外,唯一登录的用户使用远程桌面远程登录。另外,通过将登录过程中的功能,意味着您不必处理多个并发登录。但是任何一种方法都可行 –

相关问题