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

如何在WinForms的ListView中展示Active Directory用户的lastLogon属性

问题原因

  1. 空引用报错:lastLogon是AD可选属性,从未登录过域的用户该属性值为null,直接对null调用ToString()会触发空引用异常。同时lastLogon在AD中以Windows文件时间格式的长整型存储,直接转字符串也无法得到可读的时间格式。
  2. 拿到的时间不是真实最后登录时间:AD中lastLogon属性不会在多域控制器之间同步,每个域控制器仅存储用户在本节点的最后登录时间;你之前用到的lastLogonTimestamp虽然是多DC同步属性,但默认存在9~14天的同步延迟,仅用于判断长期未登录用户,不能作为实时登录时间依据。直接调用UserPrincipal.LastLogon仅会查询当前连接的单个DC记录,若用户最近在其他DC登录,拿到的就不是最新值。

解决方案

单域控制器环境(最简方案)

如果你的域只有一个域控制器,直接对可空属性做判空和时间格式化处理即可,修改你循环内的赋值逻辑:

foreach (UserPrincipal u in results)
{
    string[] arr = new string[4];
    arr[0] = u.GivenName ?? "";
    arr[1] = u.SamAccountName ?? "";
    // 对可空时间类型判空,无值时显示自定义提示
    arr[2] = u.LastLogon.HasValue ? u.LastLogon.Value.ToString("yyyy-MM-dd HH:mm:ss") : "从未登录";
    arr[3] = u.LastPasswordSet.HasValue ? u.LastPasswordSet.Value.ToString("yyyy-MM-dd HH:mm:ss") : "无记录";
    lstActiveDirectory.Items.Add(new ListViewItem(arr));
}

多域控制器环境(取最新登录时间)

如果域内有多个域控制器,需要遍历所有DC查询用户的登录时间,取最大值作为真实最后登录时间,完整调整代码如下:

if (rdbUsers.Checked == true)
{
    string strADPath = txtLDAP.Text.ToString();
    string strUsername = txtUsername.Text.ToString();
    string strPassword = txtPassword.Text.ToString();
    try
    {
        using (PrincipalContext context = new PrincipalContext(ContextType.Domain, strADPath))
        {
            if (context.ValidateCredentials(strUsername, strPassword))
            {
                try
                {
                    lstActiveDirectory.Clear();
                    lstActiveDirectory.Sorting = SortOrder.Ascending;
                    lstActiveDirectory.Columns.Add("First Name", 150);
                    lstActiveDirectory.Columns.Add("Last Name", 150);
                    lstActiveDirectory.Columns.Add("Last Logon", 180);
                    lstActiveDirectory.Columns.Add("Last Password Reset", 180);

                    // 获取当前域所有域控制器列表
                    Domain domain = Domain.GetDomain(new DirectoryContext(DirectoryContextType.Domain, strADPath, strUsername, strPassword));
                    DomainControllerCollection dcs = domain.DomainControllers;

                    PrincipalContext ctx = new PrincipalContext(ContextType.Domain, strADPath, strUsername, strPassword);
                    UserPrincipal usr = new UserPrincipal(ctx);
                    PrincipalSearcher ps = new PrincipalSearcher(usr);
                    PrincipalSearchResult<Principal> results = ps.FindAll();

                    foreach (UserPrincipal u in results)
                    {
                        DateTime? latestLogon = null;
                        // 遍历所有DC查询用户的最后登录时间,取最大值
                        foreach (DomainController dc in dcs)
                        {
                            using (PrincipalContext dcCtx = new PrincipalContext(ContextType.Domain, dc.Name, strUsername, strPassword))
                            {
                                UserPrincipal dcUser = UserPrincipal.FindByIdentity(dcCtx, IdentityType.SamAccountName, u.SamAccountName);
                                if (dcUser != null && dcUser.LastLogon.HasValue)
                                {
                                    if (!latestLogon.HasValue || dcUser.LastLogon.Value > latestLogon.Value)
                                    {
                                        latestLogon = dcUser.LastLogon.Value;
                                    }
                                }
                            }
                        }

                        string[] arr = new string[4];
                        arr[0] = u.GivenName ?? "";
                        arr[1] = u.SamAccountName ?? "";
                        arr[2] = latestLogon.HasValue ? latestLogon.Value.ToString("yyyy-MM-dd HH:mm:ss") : "从未登录";
                        arr[3] = u.LastPasswordSet.HasValue ? u.LastPasswordSet.Value.ToString("yyyy-MM-dd HH:mm:ss") : "无记录";
                        lstActiveDirectory.Items.Add(new ListViewItem(arr));
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("查询用户信息失败:" + ex.Message);
                    rdbUsers.Checked = false;
                    lstActiveDirectory.Clear();
                }
            }
            else
            {
                MessageBox.Show("服务器、用户名或密码错误");
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
        rdbUsers.Checked = false;
        lstActiveDirectory.Clear();
    }
}

注意:多DC遍历查询会增加接口耗时,用户量大的场景建议增加加载提示优化体验。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 15:36:01