在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进行关闭。同理,我们也可以获取进程的真实句柄。