如何通过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
相关产品推荐
相关产品推荐

