Click here to Skip to main content
15,881,173 members
Articles / Programming Languages / C++
Tip/Trick

Determine Time Since Last Logon

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
8 Jun 2021CPOL3 min read 5.1K   136   5   1
Find logon time on Windows 10
In this tip, you will find a stand-alone utility that can read last-logon time from the system and display time since last logon.

sample console output

Introduction

This is a stand-alone utility which can read the last-logon time from the system, and display the time since the last logon.  This can also be turned into a linkable module, by un-defining the STAND_ALONE macro at the top of the file, and exporting the two non-static functions to another file.

Background

I have a system-status monitor called DerBar that I run on my computer, showing data such as free/total memory, CPU utilization, etc. One of the pieces of data that it has always shown is Uptime, which is how long since the computer was last rebooted.

Since I was forced onto Windows 10, however, this has not quite communicated the information that I actually want to know, because it is common with Windows 10 to log out and then log back in to a session. This logout/login procedure causes all of my applications get reloaded when I log in... but does not update the Uptime fields, I guess because Windows does not consider this a reboot - but for me, this is essentially a reboot, and I want to display the time since logon, not the time since reboot.

This information - the date/time when I most recently logged in - turned out to be a bit tricky to find out, though... This article will share the techniques that I used to get this data.

I am indebted to the folks in the CodeProject discussion forums for providing some of the clues that I needed to solve this problem. Here is a link to the discussion thread.

Details

Finding the Technique

Searching for new pieces of information about Windows, usually involves studying multiple libraries and protocols; it is rare that a new piece of data is found via the same mechanism as a previous piece. In addition, I wanted a source which did not require running the utility as Administrator, if at all possible.

My initial searches for this logon information led me to a function called NetUserGetInfo(), but sadly that showed a logon date which was 77 days old, while I knew that my last logon was actually less than 1 day old. Fortunately, a CodeProject forum user informed me that this preceding function was in fact obsolete, and he pointed me to a set of functions in the LSA (Login Security Authority) module; in fact, he gave me a complete running program which gave me exactly what I wanted:

C++
INT main(void)
{
    DWORD lc = 0;
    DWORD status = 0;
    PLUID list = nullptr;

    LsaEnumerateLogonSessions(&lc, &list);
    for (DWORD i = 0; i < lc; i++)
    {
        PSECURITY_LOGON_SESSION_DATA pData;

        status = LsaGetLogonSessionData((PLUID)((INT_PTR)list + sizeof(LUID) * i), &pData);
        if (0 == status)
        {
            if (Interactive == pData->LogonType)
            {
                FILETIME ft;
                SYSTEMTIME st_utc, st_local;
                TIME_ZONE_INFORMATION tzi;

                ft.dwHighDateTime = pData->LogonTime.HighPart;
                ft.dwLowDateTime  = pData->LogonTime.LowPart;

                GetTimeZoneInformation(&tzi);
                FileTimeToSystemTime(&ft, &st_utc);
                SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_local);

                wprintf(L"UserName: %s\n", pData->UserName.Buffer);
                wprintf(L"Last logon %s: %d/%d/%d-%d:%d:%d:%d\n", tzi.StandardName, 
                st_local.wMonth, st_local.wDay, st_local.wYear, st_local.wHour, 
                st_local.wMinute, st_local.wSecond, st_local.wMilliseconds);
            }

            LsaFreeReturnBuffer(pData);
        }
    }
    return 0;
}

Compiler Anomalies

Although his utility worked perfectly, I still had a problem. I use the MinGW toolchain, rather than Microsoft tools, to build all of my applications.  It turns out that the 32-bit version of the MinGW LSA header ntsecapi.h does not contain the declarations for these functions.

After some further online research, I found a solution to this issue, which involved loading the related library secur32.dll at runtime, then using GetProcAddress() to save pointers addresses to the required functions, as shown here (error-checking removed for brevity):

C++
static bool load_LSA_library_pointers(void)
{
   //  these pointers are required for 32-buit MinGW builds
   hSecur32 = LoadLibraryW(L"secur32.dll");
   pfLELS   = (pfLsaEnumerateLogonSessions)GetProcAddress
              (hSecur32, "LsaEnumerateLogonSessions");
   pfLGLSD  = (pfLsaGetLogonSessionData)GetProcAddress(hSecur32, "LsaGetLogonSessionData");
   pfLFRB   = (pfLsaFreeReturnBuffer)GetProcAddress(hSecur32, "LsaFreeReturnBuffer");
   return true;   
}

With these function pointers available, I can then call the required functions, via their pointers:

C++
// LsaEnumerateLogonSessions(&lc, &list);
(*pfLELS)(&lc, &list);  //lint !e534
for (DWORD i = 0; i < lc; i++) {
   PSECURITY_LOGON_SESSION_DATA pData;
   time_t logon_time ;

   // status = LsaGetLogonSessionData((PLUID)((INT_PTR)list + sizeof(LUID) * i), &pData);
   status = (*pfLGLSD)((PLUID)((INT_PTR)list + _
            sizeof(LUID) * i), &pData); //lint !e732 !e737
   if (0 == status) {
      if (Interactive == pData->LogonType) { //lint !e641
         logon_time = u64_to_timet(pData->LogonTime);
         if (max_logon_time < logon_time) {
             max_logon_time = logon_time ;
         }
      }
      // LsaFreeReturnBuffer(pData);
      (*pfLFRB)(pData);
   }
}

Final Details, or What Time Is It, Anyway??

One last trivial issue was, that this operation gives me the logon time as a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601. I get current system time from time(), which returns the number of seconds since midnight, January 1, 1970. I used the following function, found at several online sources, to convert the logon time to the same timeframe as local time.

C++
static time_t u64_to_timet(LARGE_INTEGER const& ull)
{    
   return ull.QuadPart / 10000000ULL - 11644473600ULL;
}

Note that I didn't include any code to convert the logon time to my current time zone. The poster who provided the LSA function above, discussed this topic in his post, and I will do more research on this later on, but it wasn't really vital for my current requirement.

History

  • 8th June, 2021: Initial version

License

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


Written By
Software Developer (Senior) Retired
United States United States
I have worked for 25+ years as a firmware engineer, in various environments. In my private life, I create a variety of applications, including console applications in Linux and Windows, and GUI applications in Windows. All of my programs and code snippets are freeware, with source code available. All programs are written in C or C++, and built using the MinGW toolchain.

Comments and Discussions

 
PraiseMy vote of 5 Pin
Michael Haephrati10-Jun-21 6:05
professionalMichael Haephrati10-Jun-21 6:05 
Great article. The type I like!
- Michael Haephrati מיכאל האפרתי

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.