如何用C#或PowerShell获取本地机器上所有Microsoft账户及UPN?
我需要用C#获取本地机器上所有用户的UPN(用户主体名称)和Microsoft账户信息。目前whoami命令能返回自身的相关信息,但无法获取所有用户的数据:
PS C:\Windows\system32> whoami /upn my.name@mydomain.com PS C:\Windows\system32> whoami corp\my.name
我写的C#代码只能返回「计算机管理」控制台里的本地用户,拿不到Microsoft账户:
var searcher = new System.DirectoryServices.AccountManagement.PrincipalSearcher(); var userPrincipal = new UserPrincipal(new PrincipalContext(ContextType.Machine)); searcher.QueryFilter = userPrincipal; var results = searcher.FindAll();
用PowerShell的Get-LocalUser也只能看到本地用户,没有Microsoft账户的相关信息:
Name PrincipalSource ---- --------------- Administrator Local DefaultAccount Local Guest Local WDAGUtilityAccount Local
我还尝试通过注册表Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList里的SID查找用户,但执行Get-localUser -SID S-1-12-1-1111111141-1234567891-3802628514-1659616000时提示用户未找到,即使管理员权限运行也没用。
需要能通过C#或C#可调用的PowerShell方式获取Microsoft账户的方案。
解决方案
方法1:通过Windows API直接获取(C#实现)
Microsoft账户属于「已联网用户」,可以通过NetUserGetInfo和NetUserEnumAPI获取这类用户的信息,包括UPN。以下是封装后的C#代码:
首先定义API相关的结构体和导入:
using System; using System.Runtime.InteropServices; using System.Collections.Generic; public class NetworkUserHelper { private const int USER_INFO_LEVEL_10 = 10; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct USER_INFO_10 { public string usri10_name; public string usri10_full_name; // 关键字段:用户主体名称(UPN) public string usri10_upn; // 省略其他不常用字段,完整结构体可参考MSDN文档 } [DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int NetUserEnum( string servername, int level, int filter, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, IntPtr resume_handle); [DllImport("netapi32.dll", SetLastError = true)] private static extern int NetApiBufferFree(IntPtr buffer); // 用户类型过滤器:本地用户+联网账户(Microsoft账户) private const int FILTER_NORMAL_ACCOUNT = 0x00000002; private const int FILTER_TEMP_DUPLICATE_ACCOUNT = 0x00000004; public static List<UserInfo> GetAllUsersWithUPN() { var userList = new List<UserInfo>(); IntPtr bufPtr = IntPtr.Zero; int entriesRead; int totalEntries; try { // 枚举所有符合条件的用户 int result = NetUserEnum(null, USER_INFO_LEVEL_10, FILTER_NORMAL_ACCOUNT | FILTER_TEMP_DUPLICATE_ACCOUNT, out bufPtr, -1, out entriesRead, out totalEntries, IntPtr.Zero); if (result != 0) { throw new System.ComponentModel.Win32Exception(result); } IntPtr currentPtr = bufPtr; for (int i = 0; i < entriesRead; i++) { var userInfo = Marshal.PtrToStructure<USER_INFO_10>(currentPtr); userList.Add(new UserInfo { Username = userInfo.usri10_name, FullName = userInfo.usri10_full_name, UPN = userInfo.usri10_upn, IsMicrosoftAccount = !string.IsNullOrEmpty(userInfo.usri10_upn) && (userInfo.usri10_upn.Contains("@outlook.com") || userInfo.usri10_upn.Contains("@live.com") || userInfo.usri10_upn.Contains("@hotmail.com") || userInfo.usri10_upn.Contains("@microsoft.com")) }); currentPtr = IntPtr.Add(currentPtr, Marshal.SizeOf(typeof(USER_INFO_10))); } } finally { if (bufPtr != IntPtr.Zero) { NetApiBufferFree(bufPtr); } } return userList; } public class UserInfo { public string Username { get; set; } public string FullName { get; set; } public string UPN { get; set; } public bool IsMicrosoftAccount { get; set; } } }
使用示例:
var allUsers = NetworkUserHelper.GetAllUsersWithUPN(); foreach (var user in allUsers) { Console.WriteLine($"用户名: {user.Username}"); Console.WriteLine($"全名: {user.FullName}"); Console.WriteLine($"UPN: {user.UPN}"); Console.WriteLine($"是否为Microsoft账户: {user.IsMicrosoftAccount}"); Console.WriteLine("---"); }
方法2:调用PowerShell获取(C#可执行)
通过PowerShell查询Win32_UserAccount类,该类包含本地和联网用户的完整信息:
PowerShell脚本内容:
Get-CimInstance Win32_UserAccount -Filter "LocalAccount=False OR LocalAccount=True" | Select-Object Name, FullName, UserPrincipalName, LocalAccount
C#调用该脚本的代码:
using System.Management.Automation; public class PowerShellUserHelper { public static void GetUsersViaPowerShell() { using (var ps = PowerShell.Create()) { ps.AddCommand("Get-CimInstance") .AddParameter("ClassName", "Win32_UserAccount") .AddParameter("Filter", "LocalAccount=False OR LocalAccount=True"); ps.AddCommand("Select-Object") .AddParameter("Property", new string[] { "Name", "FullName", "UserPrincipalName", "LocalAccount" }); var results = ps.Invoke(); foreach (var result in results) { Console.WriteLine($"用户名: {result.Members["Name"].Value}"); Console.WriteLine($"全名: {result.Members["FullName"].Value}"); Console.WriteLine($"UPN: {result.Members["UserPrincipalName"].Value}"); Console.WriteLine($"是否本地账户: {result.Members["LocalAccount"].Value}"); Console.WriteLine("---"); } } } }
注意:运行代码需要管理员权限,且项目需引用System.Management.Automation程序集(可通过NuGet安装Microsoft.PowerShell.SDK)。
关键说明
- Microsoft账户在系统中标记为
LocalAccount=False,本地用户为LocalAccount=True。 NetUserEnum的FILTER_TEMP_DUPLICATE_ACCOUNT过滤器专门匹配Microsoft账户这类联网账户。- 注册表ProfileList中的部分SID对应已登录过的联网用户,但这类用户不会出现在本地用户列表中,需通过WMI或NetAPI获取。
内容的提问来源于stack exchange,提问作者morleyc

