Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. Windows 10 - find last logon time in C++ ??

Windows 10 - find last logon time in C++ ??

Scheduled Pinned Locked Moved C / C++ / MFC
c++visual-studiotutorialquestion
13 Posts 4 Posters 18 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Derell Licht

    I'm trying to determine this value for a system-status program that I maintain. In the past, I've just shown Uptime, but now that I'm forced to Windows 10, that isn't entirely informative!! If I log out of my machine (shutdown -l) and then log back in, Uptime doesn't get reset. So I want to add an option to show logon time vs reboot time (Uptime)... but I'm not having much success with this... I have found several articles which recommend NetUserGetInfo(), but this is returning invalid data for me... for example, I just logged out, and back in, to my machine... here is the code, followed by the results that I get:

    // Call the NetUserGetInfo function.
    dwLevel = 2;
    wchar_t username[UNLEN+1];
    DWORD username_len = UNLEN+1;
    GetUserNameW(username, &username_len);
    nStatus = NetUserGetInfo(NULL, username, dwLevel, (LPBYTE *) & pBuf);
    // skipping result checking
    pBuf2 = (LPUSER_INFO_2) pBuf;
    wprintf(L"User account name: %s\n", pBuf2->usri2_name);
    wprintf(L"Password age (seconds): %d\n", pBuf2->usri2_password_age);
    wprintf(L"Last logon (seconds since January 1, 1970 GMT): %d\n", pBuf2->usri2_last_logon);
    time_t logon_time = pBuf2->usri2_last_logon ;
    char buff[20];
    strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&logon_time));
    printf("logon time: %s\n", buff);

    The results that I get are:

    User account name: dan7m
    Password age (seconds): 10221317
    Last logon (seconds since January 1, 1970 GMT): 1616249786
    logon time: 2021-03-20 07:16:26

    Note that the logon time *actually* around 1920 on 06/05/21... So I have two questions, I guess... 1. is there some way to make this function actually work?? 2. if not, how else can I programmatically access the login time on Windows 10 64bit??

    D Offline
    D Offline
    Derell Licht
    wrote on last edited by
    #4

    BTW, I found another site which suggested a console command to display last logon: > net user dan7m | findstr /B /C:"Last logon" Last logon 03/20/21 07:16:26 Interestingly, that is showing exactly the same logon time as NetUserGetInfo(2) is showing... But that is *not* when I last logged on; it's not even when I last rebooted - Uptime is showing 4.5 days, which is correct... I think, maybe, this suggests that the 'logon' time that I'm actually looking for - which is the time since I last logged out of this session and logged in again... is maybe called something else entirely in Windows 10?? I am very confused by all this...

    1 Reply Last reply
    0
    • D Derell Licht

      I'm trying to determine this value for a system-status program that I maintain. In the past, I've just shown Uptime, but now that I'm forced to Windows 10, that isn't entirely informative!! If I log out of my machine (shutdown -l) and then log back in, Uptime doesn't get reset. So I want to add an option to show logon time vs reboot time (Uptime)... but I'm not having much success with this... I have found several articles which recommend NetUserGetInfo(), but this is returning invalid data for me... for example, I just logged out, and back in, to my machine... here is the code, followed by the results that I get:

      // Call the NetUserGetInfo function.
      dwLevel = 2;
      wchar_t username[UNLEN+1];
      DWORD username_len = UNLEN+1;
      GetUserNameW(username, &username_len);
      nStatus = NetUserGetInfo(NULL, username, dwLevel, (LPBYTE *) & pBuf);
      // skipping result checking
      pBuf2 = (LPUSER_INFO_2) pBuf;
      wprintf(L"User account name: %s\n", pBuf2->usri2_name);
      wprintf(L"Password age (seconds): %d\n", pBuf2->usri2_password_age);
      wprintf(L"Last logon (seconds since January 1, 1970 GMT): %d\n", pBuf2->usri2_last_logon);
      time_t logon_time = pBuf2->usri2_last_logon ;
      char buff[20];
      strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&logon_time));
      printf("logon time: %s\n", buff);

      The results that I get are:

      User account name: dan7m
      Password age (seconds): 10221317
      Last logon (seconds since January 1, 1970 GMT): 1616249786
      logon time: 2021-03-20 07:16:26

      Note that the logon time *actually* around 1920 on 06/05/21... So I have two questions, I guess... 1. is there some way to make this function actually work?? 2. if not, how else can I programmatically access the login time on Windows 10 64bit??

      L Offline
      L Offline
      Lost User
      wrote on last edited by
      #5

      Hi, The NetUserGetInfo function is an old derelict function. (pun intentional) Maybe you could use the Security and Identity[^] framework to get session information. Disclaimer: This is a code sample, it doesn't handle dynamic daylight saving time. Also, the pointer arithmetic could be refactored.

      #pragma comment(lib, "Secur32.lib")

      #include #include #include INT main()
      {
      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);
          }
      }
      

      }

      I noticed that the C# guys have much better time zone tools[^]. For some reason nobody invested any time into doing this for the public native API. So I whipped up something real quick for converting to an arbitrary time zone:

      //The registry 'Olson TZ structure' is partially documented here https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-time\_zone\_information --David Delaune
      typedef struct _REG_TZI_FORMAT
      {

      D 1 Reply Last reply
      0
      • L Lost User

        Hi, The NetUserGetInfo function is an old derelict function. (pun intentional) Maybe you could use the Security and Identity[^] framework to get session information. Disclaimer: This is a code sample, it doesn't handle dynamic daylight saving time. Also, the pointer arithmetic could be refactored.

        #pragma comment(lib, "Secur32.lib")

        #include #include #include INT main()
        {
        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);
            }
        }
        

        }

        I noticed that the C# guys have much better time zone tools[^]. For some reason nobody invested any time into doing this for the public native API. So I whipped up something real quick for converting to an arbitrary time zone:

        //The registry 'Olson TZ structure' is partially documented here https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-time\_zone\_information --David Delaune
        typedef struct _REG_TZI_FORMAT
        {

        D Offline
        D Offline
        Derell Licht
        wrote on last edited by
        #6

        Hey, @Randor, that works *great* !!!!!!!!!!! Thank you so much!! I do have *one* minor issue with it, though... Is this intended to be a 64-bit-only operation?? I use MinGW, not Visual C++, for all of my app development, and at this point, I still use a 32-bit compiler... However, I cannot build this with 32-bit MinGW: > g++ -Wall -O2 login_lsa.cpp -o login_lsa.exe -lsecur32 login_lsa.cpp: In function 'INT main()': login_lsa.cpp:14: error: 'LsaEnumerateLogonSessions' was not declared in this scope login_lsa.cpp:17: error: 'PSECURITY_LOGON_SESSION_DATA' was not declared in this scope login_lsa.cpp:17: error: expected ';' before 'pData' login_lsa.cpp:19: error: 'pData' was not declared in this scope login_lsa.cpp:19: error: 'LsaGetLogonSessionData' was not declared in this scope I searched through *all* .h and .hpp files in c:\mingw, and these functions were not declared anywhere... Mind ye, it *does* build and run just fine with 64-bit MinGW. (in case anyone is not familiar with MinGW, it stands for Minimal Gnu for Windows, and is a port of the GNU toolchain to Windows, using Windows libraries for most services.) My MinGW 32-bit toolchain *does* have netsecapi.h and secur32.lib (in its format), but these LSA functions appear to not be included... I also tried current TDM build of MinGW, which is gcc 10.3.0, but it does not contain these functions either...

        L 1 Reply Last reply
        0
        • D Derell Licht

          Hey, @Randor, that works *great* !!!!!!!!!!! Thank you so much!! I do have *one* minor issue with it, though... Is this intended to be a 64-bit-only operation?? I use MinGW, not Visual C++, for all of my app development, and at this point, I still use a 32-bit compiler... However, I cannot build this with 32-bit MinGW: > g++ -Wall -O2 login_lsa.cpp -o login_lsa.exe -lsecur32 login_lsa.cpp: In function 'INT main()': login_lsa.cpp:14: error: 'LsaEnumerateLogonSessions' was not declared in this scope login_lsa.cpp:17: error: 'PSECURITY_LOGON_SESSION_DATA' was not declared in this scope login_lsa.cpp:17: error: expected ';' before 'pData' login_lsa.cpp:19: error: 'pData' was not declared in this scope login_lsa.cpp:19: error: 'LsaGetLogonSessionData' was not declared in this scope I searched through *all* .h and .hpp files in c:\mingw, and these functions were not declared anywhere... Mind ye, it *does* build and run just fine with 64-bit MinGW. (in case anyone is not familiar with MinGW, it stands for Minimal Gnu for Windows, and is a port of the GNU toolchain to Windows, using Windows libraries for most services.) My MinGW 32-bit toolchain *does* have netsecapi.h and secur32.lib (in its format), but these LSA functions appear to not be included... I also tried current TDM build of MinGW, which is gcc 10.3.0, but it does not contain these functions either...

          L Offline
          L Offline
          Lost User
          wrote on last edited by
          #7

          Derell Licht wrote:

          Is this intended to be a 64-bit-only operation??

          Not sure what gave you that idea. That code will build and function correctly on both 32/64 bit platforms. The library 'Secur32.lib' was named a really long time ago and just like the path'C:\Windows\System32' the decision was made to keep the old name.

          Derell Licht wrote:

          I searched through *all* .h and .hpp files in c:\mingw, and these functions were not declared anywhere...

          I very rarely use the MinGW compiler tools. The errors you are getting are simple #include errors. I checked the msys2 source code repository and found mingw-w64/ntsecapi.h which is dated Dec 13, 2013 has all of the required function declarations. Sounds like you just need to update your development environment. Best Wishes, -David Delaune

          D 1 Reply Last reply
          0
          • L Lost User

            Derell Licht wrote:

            Is this intended to be a 64-bit-only operation??

            Not sure what gave you that idea. That code will build and function correctly on both 32/64 bit platforms. The library 'Secur32.lib' was named a really long time ago and just like the path'C:\Windows\System32' the decision was made to keep the old name.

            Derell Licht wrote:

            I searched through *all* .h and .hpp files in c:\mingw, and these functions were not declared anywhere...

            I very rarely use the MinGW compiler tools. The errors you are getting are simple #include errors. I checked the msys2 source code repository and found mingw-w64/ntsecapi.h which is dated Dec 13, 2013 has all of the required function declarations. Sounds like you just need to update your development environment. Best Wishes, -David Delaune

            D Offline
            D Offline
            Derell Licht
            wrote on last edited by
            #8

            Ahhh!! So you *are using 64-bit compiler then ?!?! mingw-w64 is the 64-bit compiler... and in my 64-bit compiler, they are also present, but not in the 32-bit compiler... Actually, though, I found a way to make this work with Mingw32... I found the clue in a page for gkrellm application, which has a support file to add support for system functions which are missing in default MinGW-32 package... What he did is use LoadLibrary() to load and obtain a handle for secur32.dll, then used GetProcAddress() to get pointers to the required functions:

            hSecur32 = LoadLibraryW(L"secur32.dll");
            if (hSecur32 != NULL)
            {
            pfLELS = (pfLsaEnumerateLogonSessions)GetProcAddress(hSecur32, "LsaEnumerateLogonSessions");
            if (pfLELS == NULL)
            {
            wprintf(L"Could not get address for LsaEnumerateLogonSessions() in secur32.dll\n");
            }

            That worked beautifully for me!! And yes, your code *does* in fact return the login times that I was looking for; Thank You again!!

            L 1 Reply Last reply
            0
            • D Derell Licht

              Ahhh!! So you *are using 64-bit compiler then ?!?! mingw-w64 is the 64-bit compiler... and in my 64-bit compiler, they are also present, but not in the 32-bit compiler... Actually, though, I found a way to make this work with Mingw32... I found the clue in a page for gkrellm application, which has a support file to add support for system functions which are missing in default MinGW-32 package... What he did is use LoadLibrary() to load and obtain a handle for secur32.dll, then used GetProcAddress() to get pointers to the required functions:

              hSecur32 = LoadLibraryW(L"secur32.dll");
              if (hSecur32 != NULL)
              {
              pfLELS = (pfLsaEnumerateLogonSessions)GetProcAddress(hSecur32, "LsaEnumerateLogonSessions");
              if (pfLELS == NULL)
              {
              wprintf(L"Could not get address for LsaEnumerateLogonSessions() in secur32.dll\n");
              }

              That worked beautifully for me!! And yes, your code *does* in fact return the login times that I was looking for; Thank You again!!

              L Offline
              L Offline
              Lost User
              wrote on last edited by
              #9

              Derell Licht wrote:

              That worked beautifully for me!! And yes, your code *does* in fact return the login times that I was looking for; Thank You again!!

              You are welcome. Best Wishes, -David Delaune

              1 Reply Last reply
              0
              • D Derell Licht

                I'm trying to determine this value for a system-status program that I maintain. In the past, I've just shown Uptime, but now that I'm forced to Windows 10, that isn't entirely informative!! If I log out of my machine (shutdown -l) and then log back in, Uptime doesn't get reset. So I want to add an option to show logon time vs reboot time (Uptime)... but I'm not having much success with this... I have found several articles which recommend NetUserGetInfo(), but this is returning invalid data for me... for example, I just logged out, and back in, to my machine... here is the code, followed by the results that I get:

                // Call the NetUserGetInfo function.
                dwLevel = 2;
                wchar_t username[UNLEN+1];
                DWORD username_len = UNLEN+1;
                GetUserNameW(username, &username_len);
                nStatus = NetUserGetInfo(NULL, username, dwLevel, (LPBYTE *) & pBuf);
                // skipping result checking
                pBuf2 = (LPUSER_INFO_2) pBuf;
                wprintf(L"User account name: %s\n", pBuf2->usri2_name);
                wprintf(L"Password age (seconds): %d\n", pBuf2->usri2_password_age);
                wprintf(L"Last logon (seconds since January 1, 1970 GMT): %d\n", pBuf2->usri2_last_logon);
                time_t logon_time = pBuf2->usri2_last_logon ;
                char buff[20];
                strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&logon_time));
                printf("logon time: %s\n", buff);

                The results that I get are:

                User account name: dan7m
                Password age (seconds): 10221317
                Last logon (seconds since January 1, 1970 GMT): 1616249786
                logon time: 2021-03-20 07:16:26

                Note that the logon time *actually* around 1920 on 06/05/21... So I have two questions, I guess... 1. is there some way to make this function actually work?? 2. if not, how else can I programmatically access the login time on Windows 10 64bit??

                D Offline
                D Offline
                Derell Licht
                wrote on last edited by
                #10

                Well, since this works so nicely, I thought I would include the complete, working demo function here... however, apparently, I cannot actually attach a file in a message, so I will just link to the file in my Github repository, in the program that it will be used in. derbar/login_lsa.cpp at master · DerellLicht/derbar · GitHub[^] Be sure to enable the STAND_ALONE macro to build stand-alone utility, either in the code, or by putting the macro on the command line, like this:

                g++ -Wall -O2 -DSTAND_ALONE=1 login_lsa.cpp -o login_lsa.exe -lsecur32

                It works very nicely!!

                L M 2 Replies Last reply
                0
                • D Derell Licht

                  Well, since this works so nicely, I thought I would include the complete, working demo function here... however, apparently, I cannot actually attach a file in a message, so I will just link to the file in my Github repository, in the program that it will be used in. derbar/login_lsa.cpp at master · DerellLicht/derbar · GitHub[^] Be sure to enable the STAND_ALONE macro to build stand-alone utility, either in the code, or by putting the macro on the command line, like this:

                  g++ -Wall -O2 -DSTAND_ALONE=1 login_lsa.cpp -o login_lsa.exe -lsecur32

                  It works very nicely!!

                  L Offline
                  L Offline
                  Lost User
                  wrote on last edited by
                  #11

                  You could post it (including a write up) in the Tips and Tricks section of Submit a new Article[^].

                  D 1 Reply Last reply
                  0
                  • L Lost User

                    You could post it (including a write up) in the Tips and Tricks section of Submit a new Article[^].

                    D Offline
                    D Offline
                    Derell Licht
                    wrote on last edited by
                    #12

                    Actually, that's a good idea... I haven't created an article here in awhile... I did so... here is a link to the article: Determine Time Since Last Logon[^]

                    1 Reply Last reply
                    0
                    • D Derell Licht

                      Well, since this works so nicely, I thought I would include the complete, working demo function here... however, apparently, I cannot actually attach a file in a message, so I will just link to the file in my Github repository, in the program that it will be used in. derbar/login_lsa.cpp at master · DerellLicht/derbar · GitHub[^] Be sure to enable the STAND_ALONE macro to build stand-alone utility, either in the code, or by putting the macro on the command line, like this:

                      g++ -Wall -O2 -DSTAND_ALONE=1 login_lsa.cpp -o login_lsa.exe -lsecur32

                      It works very nicely!!

                      M Offline
                      M Offline
                      Maximilien
                      wrote on last edited by
                      #13

                      thanks for sharing that.

                      CI/CD = Continuous Impediment/Continuous Despair

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups