在windows編程中,有時候我們會遇到需要傳入作用對象句柄的函數,如GetThreadTimes(HANDLE hThread, ...)函數,允許我們獲得句柄指定線程的運行時間。如果我們需要本線程的運行時間,那么只需要簡單調用函數
GetCurrentThread()函數即可,其會返回當前線程的“偽”句柄。類似的,我們也有函數可以獲得當前進程的句柄。
~~~
HANDLE GetCurrentThread(); //獲得當前線程偽句柄
HANDLE GetCurrentProcess(); //獲取當前進程偽句柄
~~~
偽句柄與真實句柄
注意,之前我們說的通過GetCurrentThread與GetCurrentProcess函數獲取的句柄,都是偽句柄。眾所周知,每個進程都會有一個句柄表,來保存當前進程獲取的內核對象句柄及其他信息。當進程獲取一個內核對象時,操作系統會自動將該對象信息插入當前進程的句柄表,并返回類似于索引的句柄。因此每個內核對象的句柄在不同的進程中基本是不一樣的。
但當我們調用GetCurrentThread()與GetCurrentProcess()時,其總是會返回值0xfffffffe(-2),0xffffffff(-1)。這就是所謂的偽句柄,它們并不反映真實的句柄表信息,僅用來作用于當前線程\進程本身。關于偽句柄,有以下幾點要注意:
1、偽句柄僅限作用于當前線程\進程。超出了當前線程\進程便沒有任何意義。
~~~
DWORD WINAPI childThread(PVOID pvParam)
{
HANDLE hThread = (HANDLE)pvParam;
...
GetThreadTimes(hThread, ...);
}
int _tmain(int argc, _TCHAR* argv[])
{
...
HANDLE hThread = GetCurrentThread();
CreateThread(nullptr, 0, childThread, (PVOID)hThread, 0, nullptr);
...
return 0;
}
~~~
上面代碼本意是通過子線程來獲取主線程的運行時間,但由于傳遞的是偽句柄0xfffffffe(-2),因此在子線程中,其實會獲取子線程的運行時間而非主線程。
2、偽句柄不用調用CloseHandle函數關閉
因為偽句柄不是真正的句柄,因此不需要CloseHandle來關閉。(即使調用了也沒有任何影響,CloseHandle會返回errorcode ERROR_INVALID_HANDLE)。
偽句柄轉換為真實句柄
像上面這種情況,有時候我們需要獲取線程或進程的真實句柄,那么我們可以利用函數[**DuplicateHandle**](https://msdn.microsoft.com/en-us/library/ms724251%28v=vs.85%29.aspx)來獲取。
~~~
BOOL WINAPI DuplicateHandle(
_In_ HANDLE hSourceProcessHandle,
_In_ HANDLE hSourceHandle,
_In_ HANDLE hTargetProcessHandle,
_Out_ LPHANDLE lpTargetHandle,
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwOptions
);
~~~
該函數常用來從進程A中來復制一份內核對象句柄并使B進程可用。但我們可以靈活運用一下。我們可以將上面代碼修改為
~~~
DWORD WINAPI childThread(PVOID pvParam)
{
HANDLE hThreadParent = (HANDLE)pvParam;
...
GetThreadTimes(hThreadParent, ...);
CloseHandle(hThreadParent); // 由于DuplicateHandle會增加句柄計數,因此不要忘記CloseHandle
}
int _tmain(int argc, _TCHAR* argv[])
{
...
HANDLE hThreadParent = nullptr;
// 通過DuplicateHandle獲得線程的真實句柄。
?DuplicateHandle(
GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
hThreadParent,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
CreateThread(nullptr, 0, childThread, (PVOID)hThreadParent, 0, nullptr);
...;
return 0;
}
~~~
需要注意的是,通過DuplicateHandle獲取的真實句柄,需要CloseHandle進行關閉。同理,我們也可以獲取進程的真實句柄。