HANDLE GetCurrentProcess();
HANDLE GetCurrentThread();
这两个函数都返回到主调线程的进程或线程内核对象的一个伪句柄(pseudohandle )。它们不会在主调进程的句柄表中新建句柄。而且,调用这两个函数,不会影响进程或线程内核对象的使用计数。如果调用CloseHandle,将一个伪句柄作为参数传入,CloseHandle只是简单地忽略此调 用,并返回FALSE。在这种情况下,GetLastError将返回ERROR_INVALID_HANDLE。
将伪句柄转换为真正的句柄
有时或许需要一个真正的线程句柄,而不是一个伪句柄。所谓“真正的句柄”,指的是能明确、无歧义地标识一个线程的句柄。来仔细分析下面的代码:
DWORD WINAPI ParentThread(PVOID pvParam) {
HANDLE hThreadParent = GetCurrentThread();
CreateThread(NULL, 0, ChildThread, (PVOID) hThreadParent, 0, NULL);
// Function continues...
}
DWORD WINAPI ChildThread(PVOID pvParam) {
HANDLE hThreadParent = (HANDLE) pvParam;
FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime;
GetThreadTimes(hThreadParent,
&ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime);
// Function continues...
}
能看出这个代码段的问题吗?其意图是让父线程向子线程传递一个可以标识父线程的句柄。但是,父线程传递的是一个伪句柄,
而不是一个真正的句柄。子线程开始执行时,它把这个伪句柄传给GetThreadTimes函数,这将导致子线程得到的是它自己的CPU
计时数据,而不是父线程的。之所以会发生这种情况,是因为线程的伪句柄是一个指向当前线程的句柄;换言之,
指向的是发出函数调用的那个线程。
为了修正这段代码,必须将伪句柄转换为一个真正的句柄。DuplicateHandle函数可以执行这个转换:
BOOL DuplicateHandle(
HANDLE hSourceProcess,
HANDLE hSource,
HANDLE hTargetProcess,
PHANDLE phTarget,
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwOptions);
正常情况下,利用这个函数,你可以根据与进程A相关的一个内核对象句柄来创建一个新句柄,并让它同进程B相关。但是,我们可以采取一种特殊的方式来使用它,以纠正前面的那个代码段的错误。纠正过后的代码如下:
DWORD WINAPI ParentThread(PVOID pvParam) {
HANDLE hThreadParent;
DuplicateHandle(
GetCurrentProcess(), // Handle of process that thread pseudohandle is relative to
GetCurrentThread(), // 父伪句柄
GetCurrentProcess(), // Handle of process that the new, real, thread handle is relative to
&hThreadParent, // Will receive the new, real, handle identifying the parent thread
0, // Ignored due to DUPLICATE_SAME_ACCESS
FALSE, // New thread handle is not inheritable
DUPLICATE_SAME_ACCESS); // New thread handle has same access as pseudohandle
CreateThread(NULL, 0, ChildThread, (PVOID) hThreadParent, 0, NULL);
// Function continues...
}
DWORD WINAPI ChildThread(PVOID pvParam) {
HANDLE hThreadParent = (HANDLE) pvParam;
FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime;
GetThreadTimes(hThreadParent, &ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime);
CloseHandle(hThreadParent);
// Function continues...
}
现在,当父线程执行时,它会把标识父线程的有歧义的伪句柄转换为一个新的、真正的句柄,后者明确、无歧义地标识了父线程。然后,它将这个真正的句柄传给CreateThread。当子线程开始执行时,其pvParam参数就会包含这个真正的线程句柄。在调用任何函数时,只要传入这个句 柄,影响的就将是父线程,而非子线程。 因为DuplicateHandle递增了指定内核对象的使用计数,所以在用完复制的对象句柄后,有必要 把目标句柄传给CloseHandle,
以递减对象的使用计数。前面的代码体现了这一点。调用 GetThreadTimes之后,子线程紧接着调用CloseHandle来递减父线程对象的使用计数。在这段代 码中,我假设子线程不会用这个句柄调用其他任何函数。如果还要在调用其他函数时传入父线程的句柄,那么只有在子线程完全不需要此句柄的时候,才能调用CloseHandle。
还要强调一点,DuplicateHandle函数同样可用于把进程的伪句柄转换为真正的进程句柄,如下所示:
HANDLE hProcess;
DuplicateHandle(
GetCurrentProcess(), // Handle of process that the process pseudohandle is relative to
GetCurrentProcess(), // Process' pseudohandle
GetCurrentProcess(),
&hProcess,
0,
FALSE,
DUPLICATE_SAME_ACCESS
)