GetUserName 僅回傳目前執行緒的安全性內容使用者。若程式以 SYSTEM 帳號運行(Windows 服務)、CreateProcessAsUser 建立進程,或執行緒正在模擬(Impersonation),取得的將非真正互動登入使用者。本文提供三種針對不同 OS 版本的穩健解法。
GetUserName → 回傳 "SYSTEM"。CreateProcessAsUser 啟動進程 → 回傳目標使用者,非桌面互動使用者。ImpersonateLoggedOnUser → 回傳被模擬使用者。使用 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 無 Terminal Services,無法用 WTS API。利用 explorer.exe 必定運行於互動登入使用者內容下的特性:
CreateToolhelp32Snapshot 列舉系統進程,找 explorer.exe PID。OpenProcess(PROCESS_QUERY_INFORMATION) 取得進程控制代碼。OpenProcessToken → GetTokenInformation(TokenUser) 取得 SID。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 即足夠:
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 類別中。