💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
在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进行关闭。同理,我们也可以获取进程的真实句柄。