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

如何通过ADSI从Active Directory获取计算机的lastLogon属性?

解决Active Directory计算机lastLogon属性获取问题

问题根源

你当前代码使用的是WinNT:// ADSI提供者,这个协议仅适用于本地机器或工作组环境,不支持Active Directory特有的lastLogon属性。而且通过WinNT枚举计算机时,会尝试主动连接每台目标机器,这就是你遇到关机/不存在计算机时出现严重延迟的原因。

要正确读取AD中的计算机属性,必须使用LDAP://提供者直接访问Active Directory数据库,无需连接目标计算机。

修正后的代码

以下是改用LDAP提供者获取lastLogon属性的示例代码:

#include <windows.h>
#include <activeds.h>
#include <comdef.h>
#include <iostream>
#include <wchar.h>

#pragma comment(lib, "Activeds.lib")
#pragma comment(lib, "ADSIid.Lib")

// 转换AD的FILETIME格式为可读时间字符串
void FileTimeToLocalString(FILETIME ft, wchar_t* outStr, size_t strLen) {
    SYSTEMTIME st;
    FileTimeToSystemTime(&ft, &st);
    GetLocalTime(&st); // 转换为本地时间
    swprintf_s(outStr, strLen, L"%04d-%02d-%02d %02d:%02d:%02d",
        st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
}

int main() {
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr)) {
        std::cout << "COM库初始化失败。" << std::endl;
        return 1;
    }

    // 替换为你的域LDAP路径,例如LDAP://dc=mydomain,dc=com
    IADsContainer* pContainer = NULL;
    hr = ADsOpenObject(L"LDAP://dc=mydomain,dc=com", 
        NULL, NULL, ADS_SECURE_AUTHENTICATION, 
        IID_IADsContainer, (void**)&pContainer);
    if (FAILED(hr)) {
        std::cout << "绑定Active Directory失败。" << std::endl;
        CoUninitialize();
        return 1;
    }

    // 设置搜索过滤器:只查找计算机对象
    IADsSearch* pSearch = NULL;
    hr = pContainer->QueryInterface(IID_IADsSearch, (void**)&pSearch);
    if (FAILED(hr)) {
        std::cout << "获取搜索接口失败。" << std::endl;
        pContainer->Release();
        CoUninitialize();
        return 1;
    }

    ADS_SEARCH_PARAMETERS searchParams = {0};
    searchParams.dwSize = sizeof(ADS_SEARCH_PARAMETERS);
    searchParams.lpszSearchFilter = L"(objectCategory=computer)";
    searchParams.lpszAttributes = L"name,lastLogon";
    searchParams.dwScope = ADS_SCOPE_SUBTREE;
    searchParams.fSearchCache = FALSE;

    IADsSearchResults* pResults = NULL;
    hr = pSearch->ExecuteSearch(&searchParams, &pResults);
    if (FAILED(hr)) {
        std::cout << "执行搜索失败。" << std::endl;
        pSearch->Release();
        pContainer->Release();
        CoUninitialize();
        return 1;
    }

    ADS_SEARCH_COLUMN col;
    while (pResults->GetNextRow(&col) != S_ADS_NOMORE_ROWS) {
        // 获取计算机名称
        wchar_t* computerName = NULL;
        if (col.dwADsType == ADSTYPE_CASE_IGNORE_STRING) {
            computerName = col.pADsValues->CaseIgnoreString;
        }

        // 获取lastLogon属性(FILETIME格式)
        wchar_t logonTime[64] = L"从未登录";
        if (col.dwADsType == ADSTYPE_LARGE_INTEGER) {
            FILETIME ft;
            ft.dwLowDateTime = col.pADsValues->LargeInteger.LowPart;
            ft.dwHighDateTime = col.pADsValues->LargeInteger.HighPart;
            
            // 检查是否为0(未登录过)
            if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0) {
                FileTimeToLocalString(ft, logonTime, sizeof(logonTime)/sizeof(wchar_t));
            }
        }

        if (computerName) {
            std::wcout << L"计算机: " << computerName 
                       << L" | 最后登录时间: " << logonTime << std::endl;
        }

        pResults->FreeColumn(&col);
    }

    pResults->Release();
    pSearch->Release();
    pContainer->Release();
    CoUninitialize();
    return 0;
}

关键注意事项

  • lastLogon的局限性:这个属性的值不是域内同步的,每个域控制器只存储该控制器上的登录记录。如果要获取计算机最新的登录时间,需要查询所有域控制器并取最大值。
  • 推荐使用lastLogonTimestamp:这个属性会在域内同步(默认14天同步一次),虽然不是实时的,但通常能满足大部分场景对“最后登录时间”的需求,替换代码中的lastLogon为lastLogonTimestamp即可使用。
  • 权限要求:运行程序的账号需要有读取Active Directory计算机对象属性的权限。

内容的提问来源于stack exchange,提问作者user3161924

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:34:52