Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Determine Time Since Last Logon

5.00/5 (6 votes)
8 Jun 2021CPOL3 min read 5.5K   136  
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)