Windows 10 - find last logon time in C++ ??
-
Derell Licht wrote:
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);
Not sure if it's related or not, but the two members that are being printed here are
DWORD
s so you might want to use%lu
instead. Other than that, are you running into a timezone issue? I tried your code on my Windows 10 system and it reported the correct numbers."One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
Okay, I changed them to %u, which made no difference. I'm using a 32-bit compiler, so I don't need to specify %lu to get 32-bit values. This is definitely *not* a timezone issue, since the logon time is off by 77 days, not by 8 hours!!
-
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:26Note 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??
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...
-
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:26Note 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??
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
{ -
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
{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...
-
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...
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 -
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 DelauneAhhh!! 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!!
-
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!!
-
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:26Note 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??
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!!
-
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!!
-
You could post it (including a write up) in the Tips and Tricks section of Submit a new Article[^].
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[^]
-
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!!
thanks for sharing that.
CI/CD = Continuous Impediment/Continuous Despair