首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

精準取得 Windows 系統互動登入使用者名稱:跨 Session 與服務環境解決方案

     編輯:關於VC++

GetUserName 僅回傳目前執行緒的安全性內容使用者。若程式以 SYSTEM 帳號運行(Windows 服務)、CreateProcessAsUser 建立進程,或執行緒正在模擬(Impersonation),取得的將非真正互動登入使用者。本文提供三種針對不同 OS 版本的穩健解法。

問題分析

  • 服務程式(LocalSystem)呼叫 GetUserName → 回傳 "SYSTEM"。
  • CreateProcessAsUser 啟動進程 → 回傳目標使用者,非桌面互動使用者。
  • 執行緒 ImpersonateLoggedOnUser → 回傳被模擬使用者。

方案一:Windows XP/Server 2003 以上 — WTS API

使用 WTSQuerySessionInformation 查詢目前作用中 Session 的使用者名稱:

BOOL GetLogUserXP(CString& strName) {
    LPTSTR szLogName = NULL; DWORD dwSize = 0;
    if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
        WTS_CURRENT_SESSION, WTSUserName, &szLogName, &dwSize)) {
        strName = szLogName;
        WTSFreeMemory(szLogName);
        return TRUE;
    }
    return FALSE;
}

未登入時回傳空字串。需連結 Wtsapi32.lib。

方案二:Windows 2000 — 列舉 explorer.exe 進程 Token

Windows 2000 無 Terminal Services,無法用 WTS API。利用 explorer.exe 必定運行於互動登入使用者內容下的特性:

  1. CreateToolhelp32Snapshot 列舉系統進程,找 explorer.exe PID。
  2. OpenProcess(PROCESS_QUERY_INFORMATION) 取得進程控制代碼。
  3. OpenProcessToken → GetTokenInformation(TokenUser) 取得 SID。
  4. LookupAccountSid 解析 SID 為使用者名稱與網域。
BOOL GetProcessUser(DWORD dwPID, TCHAR* szUser, DWORD cch) {
    HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPID);
    HANDLE hToken; TOKEN_USER* pTokenUser = NULL;
    OpenProcessToken(hProc, TOKEN_QUERY, &hToken);
    GetTokenInformation(hToken, TokenUser, NULL, 0, &dwNeed);
    pTokenUser = (TOKEN_USER*)new BYTE[dwNeed];
    GetTokenInformation(hToken, TokenUser, pTokenUser, dwNeed, &dwNeed);
    LookupAccountSid(NULL, pTokenUser->User.Sid, szUser, &cch, szDomain, &dwDm, &sn);
    // 清理資源...
    return TRUE;
}

缺陷:若使用者結束 explorer.exe,將無法取得名稱。

方案三:確信進程運行於互動桌面 — 直接用 GetUserName

若程式確定由使用者手動啟動(非服務、非排程任務),GetUserName 即足夠:

BOOL GetCurrProcessUser(CString& strName) {
    DWORD dwSize = MAX_PATH;
    TCHAR* psz = new TCHAR[dwSize];
    if (!GetUserName(psz, &dwSize)) {
        delete[] psz; psz = new TCHAR[dwSize];
        GetUserName(psz, &dwSize);
    }
    strName = psz; delete[] psz; return TRUE;
}

綜合建議

執行階段檢測 OS 版本(GetVersionEx),優先嘗試 WTS API(XP+),失敗再嘗試 explorer.exe 列舉法(Win2K),最後退回 GetUserName。附完整原始碼封裝於 CSecurityTool 類別中。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved