草庐IT

c - 获取当前用户的最后一次登录

coder 2023-11-11 原文

我正在尝试获取当前用户的上次登录时间。我可能是本届 session ,也可能是之前的 session 。
我正在调用 GetUserName() 来获取当前用户名。我将其输入 NetUserGetInfo() 以尝试获取上次登录时间。所有这一切都因错误 2221(未找到用户)而失败。当我尝试使用“管理员”时,它起作用了。即使我对我的用户名进行硬编码,它也会返回 2221。这就是我正在使用的:

nStatus = NetUserGetInfo(NULL, L"administrator", dwLevel, (LPBYTE *) & pBuf);

如何获取当前用户的上次登录时间?

谢谢,代码总是受欢迎的。

这是我目前使用的完整代码:

DWORD dwLevel = 2;
NET_API_STATUS nStatus;
LPTSTR sStringSid = NULL;
LPUSER_INFO_0 pBuf = NULL;
LPUSER_INFO_2 pBuf2 = NULL;
WCHAR UserName[256];
DWORD nUserName = sizeof(UserName); 

if(GetUserName(UserName, &nUserName))
{
    printf("information for %ls\n", UserName);
    nStatus = NetUserGetInfo(NULL, UserName, dwLevel, (LPBYTE *) & pBuf);
    if (nStatus == NERR_Success) 
    {
        pBuf2 = (LPUSER_INFO_2) pBuf;
        printf("\tUser account name: %ls\n", pBuf2->usri2_name);
        printf("\tLast logon (seconds since January 1, 1970 GMT): %d\n", pBuf2->usri2_last_logon);
        printf("\tLast logoff (seconds since January 1, 1970 GMT): %d\n", pBuf2->usri2_last_logoff);
    }
    else
        fprintf(stderr, "NetUserGetinfo failed with error: %d\n", nStatus);

    if (pBuf != NULL)
        NetApiBufferFree(pBuf);
}

最佳答案

您可以尝试使用其他级别作为 2,例如 11。

您也可以尝试 LsaGetLogonSessionData(请参阅 http://msdn.microsoft.com/en-us/library/aa378290.aspx)。结构 SECURITY_LOGON_SESSION_DATA 包含很多对您有帮助的信息。您可以使用 TokenStatisticsGetTokenInformation 获取 LUID(LsaGetLogonSessionData 的第一个参数)并获取 AuthenticationId TOKEN_STATISTICS 结构的 字段。

更新:我更仔细地阅读了您的代码,现在我看到了您的主要错误。函数 NetUserGetInfo 非常旧。它存在于 Windows NT 3.1 之前的时间。微软现在命名为“网络管理”的功能组名称为“LAN Manager API”。所有功能都是在没有本地登录的时候引入的。因此,您可以使用 NetUserGetInfo 并将 NULL 作为第一个参数仅在域 Controller 上。因此,在您使用域帐户登录的情况下,您应该调用 NetGetDCNameNetGetAnyDCName 或更好的 DsGetDcName 来获取域 Controller 的名称,并且使用此名称(以两个反斜杠开头)作为 NetUserGetInfo 的第一个参数。如果您使用本地工作站帐户登录,您的程序应该可以运行,但该帐户似乎必须是 UNICODE 字符串,例如 L"Administrator"而不是 "Administrator"。顺便说一下,如果我在我的 Windows 7 64 位计算机上本地登录,您的程序可以正常运行。代码

nStatus = NetUserGetInfo(NULL, L"administrator", dwLevel, (LPBYTE *) & pBuf);

也适用。

我再说一遍,我认为获取用户上次登录的最佳方法是使用 LSA(本地安全机构)API,如 LsaGetLogonSessionData。多么 promise 我为你写了一个代码示例,展示了如何在 C 中使用 LsaGetLogonSessionData:

#include <windows.h>
#include <Ntsecapi.h>
#include <Sddl.h>
#include <tchar.h>
#include <stdio.h>
//#include <ntstatus.h>
#include <malloc.h>
#include <strsafe.h>

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

// The following constant may be defined by including NtStatus.h.
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
// The LSA authentication functions are available in Unicode only.

BOOL GetLogonLUID (LUID *pLuid)
{
    BOOL bSuccess;
    HANDLE hThread = NULL;
    DWORD cbReturnLength;
    TOKEN_STATISTICS ts;

    __try {
        bSuccess = OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hThread);    // TOKEN_QUERY_SOURCE
        if (!bSuccess)
            __leave;

        cbReturnLength = sizeof(TOKEN_STATISTICS);
        bSuccess = GetTokenInformation (hThread, TokenStatistics, &ts, sizeof(TOKEN_STATISTICS), &cbReturnLength);
        if (bSuccess)
            *pLuid = ts.AuthenticationId;
    }
    __finally {
        if (hThread)
            CloseHandle (hThread);
    }

    return bSuccess;
}

void PrintUnicodeString (LPCTSTR pszPrefix, LSA_UNICODE_STRING lsaString)
{
    if (lsaString.MaximumLength >= lsaString.Length + sizeof(WCHAR) &&
        lsaString.Buffer[lsaString.Length/sizeof(WCHAR)] == L'\0')
        _tprintf (TEXT("%s: %ls\n"), pszPrefix, lsaString.Buffer);
    else if (lsaString.Length <= STRSAFE_MAX_CCH * sizeof(TCHAR)) {
        LPWSTR sz = (LPWSTR) _alloca (lsaString.Length + sizeof(WCHAR));
        StringCbCopyNW (sz, lsaString.Length + sizeof(WCHAR), lsaString.Buffer, lsaString.Length);
        _tprintf (TEXT("%s: %ls\n"), pszPrefix, sz);
    }
}

void PrintLogonType (SECURITY_LOGON_TYPE type)
{
    if (type < Interactive || type > CachedUnlock)
        // This is used to specify an undefied logon type
        _tprintf (TEXT("LogonType: UndefinedLogonType\n"));
    else {
        static LPTSTR szTypes[] = {
            TEXT("Interactive"),      // Interactively logged on (locally or remotely)
            TEXT("Network"),              // Accessing system via network
            TEXT("Batch"),                // Started via a batch queue
            TEXT("Service"),              // Service started by service controller
            TEXT("Proxy"),                // Proxy logon
            TEXT("Unlock"),               // Unlock workstation
            TEXT("NetworkCleartext"),     // Network logon with cleartext credentials
            TEXT("NewCredentials"),       // Clone caller, new default credentials
            TEXT("RemoteInteractive"),  // Remote, yet interactive. Terminal server
            TEXT("CachedInteractive"),  // Try cached credentials without hitting the net.
            // The types below only exist in Windows Server 2003 and greater
            TEXT("CachedRemoteInteractive"), // Same as RemoteInteractive, this is used internally for auditing purpose
            TEXT("CachedUnlock")        // Cached Unlock workstation
        };
        _tprintf (TEXT("LogonType: %s\n"), szTypes[(int)type-Interactive]);
    }
}

void PrintFilefime (LPCTSTR pszPrefix, const FILETIME *lpFileTime)
{
    SYSTEMTIME st;
    FILETIME ft;
    BOOL bSuccess;
    TCHAR szTime[1024], szDate[1024];

    bSuccess =  FileTimeToLocalFileTime (lpFileTime, &ft);
    if (!bSuccess)
        return;
    bSuccess = FileTimeToSystemTime (&ft, &st);
    if (!bSuccess)
        return;

    if (GetDateFormat (LOCALE_USER_DEFAULT, // or LOCALE_CUSTOM_UI_DEFAULT
                       DATE_SHORTDATE,
                       &st, NULL, szDate, sizeof(szDate)/sizeof(TCHAR)) > 0) {
        if (GetTimeFormat (LOCALE_USER_DEFAULT, // or LOCALE_CUSTOM_UI_DEFAULT
                           0, &st, NULL, szTime, sizeof(szTime)/sizeof(TCHAR)) > 0) {
            _tprintf (TEXT("%s: %s, %s\n"), pszPrefix, szDate, szTime);
        }
    }
}

int main()
{
    LUID LogonLuid; // LOGONID_CURRENT
    PSECURITY_LOGON_SESSION_DATA pLogonSessionData = NULL;
    LPWSTR pszSid = NULL;
    NTSTATUS ntStatus;

    GetLogonLUID (&LogonLuid);
    __try {
        ntStatus = LsaGetLogonSessionData (&LogonLuid, &pLogonSessionData);
        if (ntStatus == STATUS_SUCCESS) {
            if (pLogonSessionData->UserName.Length)
                PrintUnicodeString (TEXT("UserName"), pLogonSessionData->UserName);
            if (pLogonSessionData->LogonDomain.Length)
                PrintUnicodeString (TEXT("LogonDomain"), pLogonSessionData->LogonDomain);
            if (pLogonSessionData->AuthenticationPackage.Length)
                PrintUnicodeString (TEXT("AuthenticationPackage"), pLogonSessionData->AuthenticationPackage);
            PrintLogonType ((SECURITY_LOGON_TYPE)pLogonSessionData->LogonType);
            _tprintf (TEXT("Session: %d\n"), pLogonSessionData->Session);
            if (ConvertSidToStringSidW (pLogonSessionData->Sid, &pszSid))
                _tprintf (TEXT("Sid: %ls\n"), pszSid);
            if (pLogonSessionData->LogonTime.QuadPart)
                PrintFilefime (TEXT("LogonTime"), (const FILETIME *)&pLogonSessionData->LogonTime);
            if (pLogonSessionData->LogonServer.Length)
                PrintUnicodeString (TEXT("LogonServer"), pLogonSessionData->LogonServer);
            if (pLogonSessionData->DnsDomainName.Length)
                PrintUnicodeString (TEXT("DnsDomainName"), pLogonSessionData->DnsDomainName);
            if (pLogonSessionData->Upn.Length)
                PrintUnicodeString (TEXT("Upn"), pLogonSessionData->Upn);
            // one can dump more information like HomeDirectory, ProfilePath and so on
            // if _WIN32_WINNT >= 0x0600 and user login a domain
        }
    }
    __finally {
        if (pLogonSessionData)
            LsaFreeReturnBuffer(pLogonSessionData);
        if (pszSid)
            pszSid = (LPTSTR)LocalFree (pszSid);
    }
}

关于c - 获取当前用户的最后一次登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3371807/

有关c - 获取当前用户的最后一次登录的更多相关文章

  1. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  2. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  3. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  4. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  5. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  6. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  7. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  8. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  9. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  10. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

随机推荐