You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于使用USER_INFO_3::usri3_password_age可靠获取用户密码最后修改时间的Win32 API问题

关于使用USER_INFO_3::usri3_password_age可靠获取用户密码最后修改时间的Win32 API问题

嘿,这个问题我之前帮朋友排查过,太懂这种差1秒的糟心了!咱们先把问题的根儿找出来,再给你靠谱的解决方案。

首先你碰到的1秒误差,核心原因是竞态条件(race condition):你先调用NetUserGetInfo()拿到usri3_password_age,然后再调用time(NULL)获取当前时间——这两个系统调用不是原子操作,中间哪怕只过了0.5秒,等你调用time(NULL)的时候可能刚好跨到下一秒,自然计算出来的密码修改时间就差了1秒。说白了,time()和NetUserGetInfo()本身都没做错,错在你用了两个不同时间点的数值来做计算。

那该怎么解决?给你两个方案,都符合你要的LDAP-agnostic的要求:

方案一:直接用USER_INFO_10获取绝对时间戳(最推荐)

其实Win32 API早就给你准备好了更直接的方式——用NetUserGetInfo()的信息级别10(LEVEL 10),对应的USER_INFO_10结构体里有个usri10_password_last_set字段,这是一个FILETIME结构,直接存储了密码最后修改的绝对时间戳,完全不用你自己计算,从根源上避免了时间差的问题。

举个简单的代码例子:

USER_INFO_10 *pUserInfo10 = NULL;
NET_API_STATUS status = NetUserGetInfo(
    NULL,            // 本地机器填NULL,域用户填域名或DC地址
    L"目标用户名",   // 要查询的用户名
    10,              // 指定信息级别为10
    (LPBYTE*)&pUserInfo10
);

if (status == NERR_Success && pUserInfo10 != NULL) {
    SYSTEMTIME st;
    // 把FILETIME转成易读的SYSTEMTIME格式
    if (FileTimeToSystemTime(&pUserInfo10->usri10_password_last_set, &st)) {
        // 这里可以根据需求转成本地时间或者UTC时间
        wprintf(L"密码最后修改时间:%04d-%02d-%02d %02d:%02d:%02d\n",
            st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
    }
    // 别忘了释放API分配的内存
    NetApiBufferFree(pUserInfo10);
} else {
    wprintf(L"查询失败,错误码:%d\n", status);
}

这个方案兼容性很好,从Windows 2000开始就支持,不管是本地用户还是域用户都能用,完全不用碰LDAP,完美符合你的要求。

方案二:如果必须用USER_INFO_3,优化时间同步

要是你因为某些限制不得不使用USER_INFO_3,那就要想办法把获取usri3_password_age和时间戳的操作尽量同步。比如在调用NetUserGetInfo()的前后都获取高精度时间,取中间值来计算:

FILETIME ftBefore, ftAfter;
USER_INFO_3 *pUserInfo3 = NULL;

// 调用前先记时间
GetSystemTimePreciseAsFileTime(&ftBefore);
NET_API_STATUS status = NetUserGetInfo(NULL, L"目标用户名", 3, (LPBYTE*)&pUserInfo3);
// 调用后立刻记时间
GetSystemTimePreciseAsFileTime(&ftAfter);

if (status == NERR_Success && pUserInfo3 != NULL) {
    // 把FILETIME转成time_t格式(可自行实现转换函数或调用系统API)
    time_t timeBefore = FileTimeToTimeT(&ftBefore);
    time_t timeAfter = FileTimeToTimeT(&ftAfter);
    // 取调用NetUserGetInfo的中间时刻时间
    time_t apiCallTime = (timeBefore + timeAfter) / 2;
    // 计算密码修改时间
    time_t passwordChangeTime = apiCallTime - pUserInfo3->usri3_password_age;
    
    // 后续处理...
    NetApiBufferFree(pUserInfo3);
}

不过这个方案还是不如方案一省心,毕竟还要自己处理时间转换,而且理论上还是存在极微小的误差,只是被缩小到可以忽略的程度。

最后再补一句:你完全不用换其他大的API体系,NetUser系列就足够解决问题,关键是选对信息级别,别再用相对时间自己算了。

备注:内容来源于stack exchange,提问作者Opux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 15:34:57