LastLogon des Benutzers feststellen  
Frank Dzaebel, erstellt am: 21.07.2008, zuletzt geändert:  21.07.2008
Kategorie: C# Sprache, .NET-Version: 2.0, [Download]

Es gibt mehrere Methoden, die Zeit des letzten Logons eines Users festzustellen. Während Verfahren über DirectoryEntry langsam sind, kann man über die NetUserGetInfo API ein performanteres Verhalten erreihen. Allerdings funktioniert das Verfahren mit DirectoryEntry über LDAP auch mit Active Directory.


Form1.cs:
using System;
using System.Windows.Forms;

namespace UserLogonInfo
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      DateTime? lastLogon; bool ok;

      ok = UserInfos.GetLastLogon_withDirectoryEntry(Environment.MachineName,
         Environment.UserName, out lastLogon); // langsam

      //ok = UserInfos.GetLastLogonTime_withAPI(Environment.MachineName,
      // Environment.UserName, out lastLogon); // schnell

      if (lastLogon.HasValue)
        MessageBox.Show("Letzte Logon-Zeit: " + lastLogon.ToString());
      else
        MessageBox.Show("Logon-Info nicht vorhanden");
    }
  }
}


UserInfos.cs:
using System;
using System.Runtime.InteropServices;
using LPWSTR = System.String;
using DWORD = System.Int32;
using System.DirectoryServices;

class UserInfos
{
  #region PInvoke Deklarationen

  /// <summary>
  /// [NetUserGetInfo Function (Windows)]
  /// http://msdn.microsoft.com/en-us/library/aa370654(VS.85).aspx
  /// </summary>
  [DllImport("Netapi32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
  private extern static int NetUserGetInfo(
      [MarshalAs(UnmanagedType.LPWStr)] string serverName,
      [MarshalAs(UnmanagedType.LPWStr)] string userName,
      UserInfoNr level, out IntPtr bufPtr);

  [DllImport("Netapi32.dll", SetLastError = true)]
  private extern static int NetApiBufferFree(IntPtr buffer);

  /// <summary>
  /// [USER_INFO_4 Structure (Windows)]
  /// http://msdn.microsoft.com/en-us/library/aa371339(VS.85).aspx
  /// </summary>
  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  public struct USER_INFO_4
  {
    public LPWSTR name; public LPWSTR password;
    public DWORD password_age; public DWORD priv;
    public LPWSTR home_dir; public LPWSTR comment;
    public DWORD flags; public LPWSTR script_path;
    public DWORD auth_flags; public LPWSTR full_name;
    public LPWSTR usr_comment; public LPWSTR parms;
    public LPWSTR workstations; public DWORD last_logon;
    public DWORD last_logoff; public DWORD acct_expires;
    public DWORD max_storage; public DWORD units_per_week;
    public IntPtr logon_hours;      //PBYTE
    
    public DWORD bad_pw_count; public DWORD num_logons;
    public LPWSTR logon_server; public DWORD country_code;
    public DWORD code_page; public IntPtr user_sid;   //PSID
    public DWORD primary_group_id; public LPWSTR profile;
    public LPWSTR home_dir_drive; public DWORD password_expired;
  }

  enum UserInfoNr : int
  {
    /// <summary>[USER_INFO_4 Structure (Windows)]</summary>
    Nr_4 = 4
  }

  #endregion

  /// <summary>Wird zurückgegeben, wenn NetUserGetInfo erfolgreich ist.</summary>
  const int NetReturnSuccess = 0;

  /// <summary>Letzte Logon-Zeit des Users.
  /// (recht schnell im Gegensatz zu der Methode mit DirectoryEntry)</summary>
  static public bool GetLastLogonTime_withAPI(string machineName, string accountName, out DateTime? lastLogon)
  {
    USER_INFO_4 userInfo4 = new USER_INFO_4();
    IntPtr uinfo4; lastLogon = null;
    int ret = NetUserGetInfo(machineName, accountName, UserInfoNr.Nr_4, out uinfo4);
    if (ret == NetReturnSuccess)
    {
      userInfo4 = (USER_INFO_4)Marshal.PtrToStructure(uinfo4, typeof(USER_INFO_4));
      lastLogon = new DateTime(1970, 1, 1).AddSeconds(userInfo4.last_logon).ToLocalTime();
    }
    NetApiBufferFree(uinfo4);
    uinfo4 = IntPtr.Zero;
    return (ret == NetReturnSuccess);
  }

  /// <summary>LastLogon über DirectoryEntry (ziemlich langsam)
  /// Geht aber auch für lokale Maschinen</summary>
  static public bool GetLastLogon_withDirectoryEntry(string machineName,
    string userName, out DateTime? lastLogon)
  {
    lastLogon = null;
    try
    {
      string fullName = machineName + "/" + userName;
      DirectoryEntry user = new DirectoryEntry(@"WinNT://" + fullName);
      lastLogon = (DateTime)user.Properties["LastLogin"].Value;
    }
    catch { }
    return false;
  }
}