windows提供了一些函数,使线程能够很容易引用它的进程或线程的内核对象。
HANDLE GetCurrentProcess();
HANDLE GetCurrentThread();
上面这两个函数都能返回调用线程的进程的伪句柄或线程内核对象的伪句柄。这些函数并不在创建进程的句柄表中创建新句柄。还有,调用这些函数对进程或线程内核对象的使用计数没有任何影响。如果调用 CloseHandle,将伪句柄作为参数来传递,那么 CloseHandle就会忽略该函数的调用并返回FALSE 。
有时可能需要获得线程的实句柄而不是伪句柄。所谓的“实句柄”,我是指用来明确标识一个独一无二的线程句柄。请看下面代码:
DWORD WINAPI ParentThread(PVOID pvParam)
{
HANDLE hParent = GetCurrentThread();
CreateThread(NULL,0,ChildThread,(PVOID)hParent,0,NULL);
}
DWORD WINAPI ChildThread(PVOID pvParam)
{
HANDLE hParent = (HANDLE)pvParam;
FILETIME ftCreationTime,ftExitTime,ftKernelTime,ftUserTime;
GetThreadTimes(hParent,&ftCreationTime,&ftExitTime,&ftKernelTime,&ftUserTime);
}
你能发现这个代码段存在的问题吗?这个代码的目的是让父线程给子线程传递一个线程句柄,以标识父线程。但是,父线程传递了一个伪句柄,而不是一个实句柄。当子线程开始运行时,它将一个伪句柄传递给 G e t T h r e a d Ti m e函数,使子线程得到它自己的 C P U时间,而不是父线程的C P U时间。出现这种情况的原因是线程的伪句柄是当前线程的句柄,也就是说,它是调用函数的线程的句柄。
为了修改这个代码,必须将伪句柄变成实句柄。 D u p l i c a t e H a n d l e函数能够执行这一转换:

通常可以使用这个函数,用与另一个进程相关的内核对象来创建一个与进程相关的新句柄。
然而,可以用一种特殊的方法来使用这个函数,以便修改上面介绍的代码段。正确的代码段应
该是下面的样子:
DWORD WINAPI ParentThread(PVOID pvParam)
{
HANDLE hParent;
DuplicateHandle(
GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
&hParent,
0,
FALSE,
DUPLICATE_SAME_ACCESS
);
CreateThread(NULL,0,ChildThread,(PVOID)hParent,0,NULL);
}
DWORD WINAPI ChildThread(PVOID pvParam)
{
HANDLE hParent = (HANDLE)pvParam;
FILETIME ftCreationTime,ftExitTime,ftKernelTime,ftUserTime;
GetThreadTimes(hParent,&ftCreationTime,&ftExitTime,&ftKernelTime,&ftUserTime);
CloseHandle(hParent);
}
当父线程运行时,它就将标识父线程所用的伪句柄转换成明确标识父线程所用的新的实句柄。同时它将这个实句柄传递给 C r e a t e T h r e a d。当子线程启动运行时,它的p v P a r a m参数包含了线程的实句柄。对传递该句柄的函数的任何调用都将影响父线程而不是子线程。
由于D u p l i c a t e H a n d l e会递增特定对象的使用计数,因此当完成对复制对象句柄的使用时,应该将目标句柄传递给 C l o s e H a n d l e,从而递减对象的使用计数,这一点很重要。上面的代码
段已经显示出这一点。在调用 G e t T h r e a d Ti m e s之后,紧接着子线程调用C l o s e H a n d l e,以便递减
父线程对象的使用计数。在这个代码段中,我假设子线程不使用该句柄来调用任何其他函数。
如果其他函数被调用,以便传递父线程的句柄,那么在子线程不再需要该句柄之前,不应该调
用C l o s e H a n d l e 。