CheckTokenMembership 是判断当前进程是否属于 BUILTIN\Administrators 组最轻量、最可靠的方式,它不触发UAC弹窗、不依赖清单、从XP SP2起全平台可用,且仅检查线程令牌中Administrators SID是否启用;需传nullptr自动使用当前令牌,用AllocateAndInitializeSid构造并FreeSid释放SID,结果存于输出参数而非返回值。

直接用 CheckTokenMembership 判断当前进程是否属于 BUILTIN\Administrators 组,是最轻量、最可靠的方式。别碰 IsUserAnAdmin() —— 它已被微软标记为过时,UAC 下返回值不可信。
为什么 CheckTokenMembership(nullptr, ...) 是首选
这个函数不打开令牌句柄、不触发 UAC 提权弹窗、不依赖 manifest 清单,且从 Windows XP SP2 起全平台可用。它只做一件事:查当前线程的访问令牌里有没有 Administrators 组 SID。
-
CheckTokenMembership第一个参数传nullptr,表示自动使用当前线程令牌,省去OpenProcessToken和权限申请 - 第二个参数必须是用
AllocateAndInitializeSid构造的 SID,不能硬编码或复用其他地方的 SID - 必须调用
FreeSid释放 SID,否则每次检查都泄漏内存 - 函数返回值只表示“调用是否成功”,真正结果在输出参数(如
&bIsAdmin)里,不是返回值本身
CheckTokenMembership 常见错误写法
最容易栽在“以为返回值就是结果”上。比如下面这段代码逻辑是错的:
BOOL b = CheckTokenMembership(nullptr, pAdminSid, &b); // ❌ 把输出参数和返回值混用
正确写法必须分开处理:
立即学习“C++免费学习笔记(深入)”;
- 先声明独立的
BOOL bIsAdmin = FALSE - 调用
CheckTokenMembership(nullptr, pAdminSid, &bIsAdmin) - 再用
bIsAdmin != FALSE判断结果,而不是看函数返回值 - 如果函数返回
FALSE(调用失败),应默认无权限,不能跳过判断
对比 TokenElevation 检查方式
有人用 GetTokenInformation(hToken, TokenElevation, ...) 查 TokenIsElevated,这其实检查的是“是否已提权”,而非“是否属于管理员组”。两者语义不同:
- 普通用户用“以管理员身份运行”启动程序 →
TokenIsElevated == TRUE,但CheckTokenMembership仍返回FALSE(因为令牌不属于 Administrators 组) - 管理员用户未提权运行程序 →
TokenIsElevated == FALSE,但CheckTokenMembership返回TRUE - UAC 关闭时,
TokenIsElevated始终为TRUE,失去区分意义;而CheckTokenMembership行为稳定不变
多数业务场景要的是“能否执行需管理员权限的操作”,这取决于是否在 Administrators 组,而不是当前有没有提权窗口。
跨进程检查要额外注意句柄权限
如果目标不是当前进程,而是其他 PID 的进程,必须用 OpenProcess(PROCESS_QUERY_INFORMATION, ...) 获取句柄,再用 OpenProcessToken(hProcess, TOKEN_QUERY, ...)。这里容易漏掉两点:
-
TOKEN_QUERY权限不够 —— 某些系统策略下还需TOKEN_DUPLICATE才能读取完整令牌信息 - 目标进程可能已退出,
OpenProcess返回NULL,必须检查并提前返回 - 64 位进程查 32 位进程(或反之)一般没问题,但若启用了
SeDebugPrivilege,需确保调用方有对应特权,否则OpenProcessToken失败且GetLastError()返回ERROR_ACCESS_DENIED
真实环境中,CheckTokenMembership 的输入 SID 构造和释放逻辑极易被忽略,一不留神就内存泄漏或崩溃 —— 这比功能逻辑本身更常出问题。


















