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

提升权限场景下获取SHGetFolderPath所需的当前用户登录安全令牌

管理员权限进程获取当前登录用户文件夹路径解决方案

原有代码故障原因

  • WTSQueryUserToken 接口要求调用进程持有SeTcbPrivilege特权(即作为操作系统的一部分特权),默认提升的管理员权限进程未启用该特权,会导致调用失败
  • 直接对PWTS_SESSION_INFO指针做自增操作,后续释放内存时会因指针偏移产生异常
  • 未做API返回值校验,无法定位具体错误点
  • 未释放WTS接口分配的内存,存在内存泄漏

完整可行实现代码

#include <windows.h>
#include <wtsapi32.h>
#include <shlobj.h>
#include <userenv.h>

#pragma comment(lib, "wtsapi32.lib")
#pragma comment(lib, "userenv.lib")
#pragma comment(lib, "shell32.lib")

// 调整进程特权的辅助函数
BOOL EnablePrivilege(LPCWSTR szPrivName, BOOL bEnable)
{
    HANDLE hToken = NULL;
    LUID luid;
    TOKEN_PRIVILEGES tp = {0};

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
        return FALSE;

    if (!LookupPrivilegeValueW(NULL, szPrivName, &luid))
    {
        CloseHandle(hToken);
        return FALSE;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;

    BOOL bRet = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
    CloseHandle(hToken);
    return bRet && (GetLastError() == ERROR_SUCCESS);
}

BOOL GetCurrentLoginUserFolder(REFKNOWNFOLDERID rfid, PWSTR* ppszPath)
{
    BOOL bRet = FALSE;
    HANDLE hUserToken = NULL;
    PWTS_SESSION_INFO pSessions = NULL;
    DWORD dwSessionCount = 0;
    DWORD dwActiveSessionId = 0xFFFFFFFF;

    // 先开启需要的特权
    if (!EnablePrivilege(SE_TCB_NAME, TRUE))
        return FALSE;

    // 枚举所有会话
    if (!WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessions, &dwSessionCount))
        goto Cleanup;

    // 查找当前活动的会话
    for (DWORD i = 0; i < dwSessionCount; i++)
    {
        if (pSessions[i].State == WTSActive)
        {
            dwActiveSessionId = pSessions[i].SessionId;
            break;
        }
    }

    if (dwActiveSessionId == 0xFFFFFFFF)
        goto Cleanup;

    // 查询活动会话的用户令牌
    if (!WTSQueryUserToken(dwActiveSessionId, &hUserToken))
        goto Cleanup;

    // 通过用户令牌获取对应路径
    if (SHGetKnownFolderPath(rfid, 0, hUserToken, ppszPath) == S_OK)
        bRet = TRUE;

Cleanup:
    if (hUserToken)
        CloseHandle(hUserToken);
    if (pSessions)
        WTSFreeMemory(pSessions);
    // 关闭之前开启的特权
    EnablePrivilege(SE_TCB_NAME, FALSE);
    return bRet;
}

// 调用示例
int main()
{
    PWSTR pszDesktopPath = NULL;
    // 可替换为其他FOLDERID_*常量获取对应系统文件夹路径
    if (GetCurrentLoginUserFolder(FOLDERID_Desktop, &pszDesktopPath))
    {
        // 业务逻辑处理
        CoTaskMemFree(pszDesktopPath);
    }
    return 0;
}

注意事项

  • 该实现适配单用户登录的常规桌面场景,多用户同时登录的服务器场景需要根据业务需求调整会话选择逻辑
  • 路径获取成功后需要调用CoTaskMemFree释放SHGetKnownFolderPath分配的内存
  • 所有内核对象句柄、WTS分配的内存都需要按示例完成释放,避免资源泄漏
  • 如果需要调用其他用户上下文的接口,可以拿到hUserToken后调用ImpersonateLoggedOnUser模拟用户身份,调用完成后执行RevertToSelf回到原进程上下文即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 19:15:03