Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C#

How to get the REAL lastlogon datetime from Active Directory

Rate me:
Please Sign up or sign in to vote.
4.67/5 (7 votes)
21 Mar 2013CPOL1 min read 50.3K   10   3
This article describes how to get the real lastlogon datetime from an user from Active Directory and how to use custom Active Directory attributes.

Introduction

This article describes how to get the real last-logon date-time from an user from Active Directory and how to use custom Active Directory attributes.

Background   

The .NET System.DirectoryServices.AccountManagement classes (from Framework 3.5) provide some neat functionality to access active directory users in a rather simple way. Retrieving a user is as simple as this:

C#
using (var adContext = new PrincipalContext(ContextType.Domain, ADDomainName, ADContainer)
{
    username = "ADusername";
    var foundUser = UserPrincipal.FindByIdentity(adContext, username);
    if (foundUser != null)
    {
        // do all sorts of neat stuff with user object
    }
} 

Now you would expect (as any other normal human being) that getting the lastlogon datetime from the Active Directory user is as simple as this from the UserPrincipal object we just created:

var lastlogonDateTime = founduser.LastLogon;  

 But unfortunately it isn't. After some research I found the following: 'Because the lastLogon attribute is not replicated throughout the domain, if our new user has never logged on to domain controller B then domain controller B will have no knowledge of the user’s last logon time.' 

Anyway, anyhow, it doesn't come up with the right last-logon time. It simply doesn't work, how do we solve this? This gave me a severe headache. 

It is there, but it is hidden. You must retrieve the right time by using extension attributes. By extending the Userprincipal class, you can access these attributes. Not only the real lastlogon time, but also different properties like fax number and so on.  

Using the code  

Below is my extended Userprincipal class which you can use instead of UserPrincipal

C++
using System;
using System.DirectoryServices.AccountManagement;
using System.Reflection;

namespace MyNameSpace
{
    /// <summary>
    /// some properties must be retrieved by getting extended properties
    /// this is unfortunately a protected method and only accessible
    /// by using our own derived user from UserPrincipal
    /// </summary>
    [DirectoryRdnPrefix("CN")]
    [DirectoryObjectClass("user")]
    public class UserPrincipalExtended : UserPrincipal
    {
        public UserPrincipalExtended(PrincipalContext context) : base(context) { }

        public UserPrincipalExtended(PrincipalContext 
            context, 
            string samAccountName, 
            string password,
            bool enabled)
            : base(context, samAccountName, password, enabled) { }

        public static new UserPrincipalExtended FindByIdentity(PrincipalContext context,
                                                       string identityValue)
        {
            return (UserPrincipalExtended)FindByIdentityWithType(context,
                                                         typeof(UserPrincipalExtended),
                                                         identityValue);
        }

        public static new UserPrincipalExtended FindByIdentity(PrincipalContext context,
                                                       IdentityType identityType,
                                                       string identityValue)
        {
            return (UserPrincipalExtended)FindByIdentityWithType(context,
                                                         typeof(UserPrincipalExtended),
                                                         identityType,
                                                         identityValue);
        }

        #region custom attributes
        [DirectoryProperty("RealLastLogon")]
        public DateTime? RealLastLogon
        {
            get
            {
                if (ExtensionGet("LastLogon").Length > 0)
                {
                    var lastLogonDate = ExtensionGet("LastLogon")[0];
                    var lastLogonDateType = lastLogonDate.GetType();

                    var highPart = (Int32)lastLogonDateType.InvokeMember("HighPart", 
                        BindingFlags.GetProperty, null, lastLogonDate, null);
                    var lowPart = (Int32)lastLogonDateType.InvokeMember("LowPart", 
                        BindingFlags.GetProperty | BindingFlags.Public, null, lastLogonDate, null);

                    var longDate = ((Int64)highPart << 32 | (UInt32)lowPart);

                    return longDate > 0 ? (DateTime?) DateTime.FromFileTime(longDate) : null;
                }

                return null;
            }
        }
        #endregion
    }
}

This will give you the proper LastLogon datetime. And you can add properties to retrieve other (custom) attributes for future purposes: 

C#
[DirectoryProperty("HomePage")]
public string HomePage
{
    get
    {
        if (ExtensionGet("HomePage").Length != 1)
            return null;
        return (string)ExtensionGet("HomePage")[0];
    } 
    set { this.ExtensionSet("HomePage", value); }
}  

This is very convenient as the original Userprincipal class only exposes about 10% of the active directory attributes.  

Mind you use the attributes above the class UserPrincipalExtended. Copy the class literally in your project, change only the namespace and everything will work fine.  Good luck! 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNot really working Pin
maro00925-Mar-13 13:17
maro00925-Mar-13 13:17 
GeneralLastLogonTimeStamp Pin
Narfix25-Mar-13 1:13
professionalNarfix25-Mar-13 1:13 
GeneralMy vote of 4 Pin
Prasad Khandekar21-Mar-13 10:09
professionalPrasad Khandekar21-Mar-13 10:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.