在Windows NT/2000/XP上編寫程序時,有時會需要我們獲取與當前調用線程關聯的用戶名和域名(domain),本文下面將示范在Windows NT/2000/XP環境裡如何使用Win32 API有關安全的函數來獲取用戶名和域名。
在Windows NT之前,一般都假設某個線程是運行在登錄用戶的帳號之下。但Windows NT問世以後,允許線程可以在多個安全上下文中運行,言下之意就是一個線程對多個用戶。例如,在客戶/服務器(C/S)應用中,服務器的某個線程可以通過 ImpersonateNamedPipeClient 函數模仿一個客戶。在這種情況下,它運行在該客戶端的用戶上下文中。另一個運行在不同安全上下文中的線程例子是服務線程,它具備 NT AUTHORITY 域名和 SYSTEM 用戶名,並且運行在本地系統賬號之下。
如果當前線程的用戶名和域名兩者你都需要,則必須首先調用 OpenThreadToken 打開與某個線程關聯的存取令牌: if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken)) {
如果與當前線程關聯的存取令牌不存在,則調用 OpenProcessToken 獲取與當前進程關聯的存取令牌。
if(GetLastError() == ERROR_NO_TOKEN) {
//
// 如果得不到線程令牌,則試圖打開進程令牌。
//
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken )) {
......
}
}
else {
//
// 存取線程令牌出錯。
//
......
}
......
}
一旦得到線程或進程的存取令牌,便可以調用 GetTokenInformation 函數從線程的存取令牌中獲取用戶安全標示,也就是 SID:
bSuccess = GetTokenInformation(hToken,
最後,調用 LookupAccountSid 函數獲取與該SID關聯的賬號名和域名,詳細實現細節請參考本文的例子代碼。圖一是例子程序運行的畫面:
TokenUser,
InfoBuffer,
cbInfoBuffer,
&cbInfoBuffer);
if(!bSuccess) {
......
}
else {
bRet = LookupAccountSid(NULL, ((PTOKEN_USER)InfoBuffer)->User.Sid,
UserName,
cchUserName,
DomainName,
cchDomainName,
&snu );
if (!bRet) {
......
CloseHandle(hToken);
......
}
else {
// 顯示得到的 用戶名和域名
SetDlgItemText(IDC_STATIC_USRN,UserName);
SetDlgItemText(IDC_STATIC_DOMAIN,DomainName);
......
}
......
}
圖一 獲取用戶名和域名例子程序
在編寫本文例子程序時,本來想寫一個簡單的控制台程序。但是很不幸,我碰到這樣一個百思不得其解的問題:在控制台程序中調用LookupAccountSid函數,總是得到一個失敗的返回,GetLastError() 函數指示的出錯代碼是14,也就是"存儲器不足,無法完成此操作。"但在非控制台程序中調用是沒有問題的。目前我還在繼續琢磨這個問題。如果哪位已經知道其中的原因,不妨指點一二,以免我 再走彎路……。為了交流方便,我將控制台程序和非控制台程序的源代碼都提供出來,以便參考。
上面提到的幾個32位函數不支持 Windows 9x。如果想存取Windows 95 或 Windows 98 系統中的用戶名和交互用戶的域信息,則必須調用16位LAN Manager 函數。詳細實現細節請參考MSDN庫的有關內容。
注意:如果僅僅需要獲取用戶名,那麼調用 GetUserName 足矣,這個函數支持 Windows 9x、Windows NT 以及 Windows 2000。在 Windows NT 和 Windows 2000 系統中,此函數首先檢查調用線程是否具備專門的存取令牌,如果得到令牌,則返回與調用線程關聯的用戶名,否則,返回與調用進程關聯的用戶名。
最後,祝大家身體健康!編程愉快!
本文配套源碼