在使用很多函数的时候,我们都需要获得一个对象的句柄,而某些函数返回的是伪句柄。伪句柄本身不会打开内核对象的句柄表,因此内核对象的使用计数就不会增加。它本身就只指向调用它的主调进程或线程。会因为调用者的不同而改变,比如:调用者A使用一个伪句柄,这个句柄指向调用者A,而调用者A将该句柄传递给调用者X,则这个句柄就指向调用者X。
GetCurrentThread函数和GetCurrentProcess函数的返回值都是伪句柄,在本进程(线程)内该值固定为0xFFFFFFFF(进程)、0xFFFFFFFE(线程)
下面是一段小测试:
#include <windows.h>
#include <stdio.h>
DWORD WINAPI ChildThread(LPVOID lpThreadparam)
{
HANDLE hParenet=(HANDLE)lpThreadparam;
HANDLE hChild=GetCurrentThread();
return 0;
}
int main()
{
HANDLE hThread=GetCurrentProcess();
HANDLE hParent=GetCurrentThread();
DWORD dwThreadId;
hThread=CreateThread(
NULL, // default security attributes
0, // use default stack size
ChildThread, // thread function
(LPVOID)hParent, // argument to thread function
0, // use default creation flags
&dwThreadId); // returns the thread identifier
return 0;
}
一段很简单的代码,我们的目的就是具体看看“伪句柄”,经过设断点跟踪如下图:
进入线程之后:
我们发现,无论在主线程还是子线程,通过GetCurrentThread得到的都为0xfffffffe
接下来我们再看到具体的:
#include <windows.h>
#include <stdio.h>
DWORD WINAPI ChildThread(LPVOID lpThreadparam)
{
HANDLE hChild=GetCurrentThread();
HANDLE hThreadParent = (HANDLE)lpThreadparam;
FILETIME ftCreationTime,ftExitTime,ftKernelTime,ftUserTime;
GetThreadTimes(hThreadParent,&ftCreationTime,&ftExitTime,&ftKernelTime,&ftUserTime);
if(CloseHandle(hThreadParent))
{
printf("close handle2 successful\n");
}
SYSTEMTIME systime1,systime2,systime3; //定义系统时间结构
FileTimeToSystemTime(&ftCreationTime, &systime1);//将文件时间 转换为 系统时间
printf("这个是在子线程里的~调用传来的参数显示父进程创建的时间:");
printf("%d 时%d分 %d秒\n",systime1.wHour,systime1.wMinute,systime1.wSecond);
return 0;
}
int main()
{
HANDLE hThread=GetCurrentProcess();
HANDLE hParent=GetCurrentThread();
DWORD dwThreadId;
FILETIME ftCreationTime,ftExitTime,ftKernelTime,ftUserTime;
GetThreadTimes(hParent,&ftCreationTime,&ftExitTime,&ftKernelTime,&ftUserTime);
if(CloseHandle(hParent))
{
printf("close handle1 successful\n");
}
SYSTEMTIME systime1,systime2,systime3; //定义系统时间结构
FileTimeToSystemTime(&ftCreationTime, &systime1);//将文件时间 转换为 系统时间
printf("这个是在父线程里的~获得父线程创建时间:");
printf("%d 时%d分 %d秒\n",systime1.wHour,systime1.wMinute,systime1.wSecond);
Sleep(5000);
hThread=CreateThread(
NULL, // default security attributes
0, // use default stack size
ChildThread, // thread function
(LPVOID)hParent, // argument to thread function
0, // use default creation flags
&dwThreadId); // returns the thread identifier
system("pause");
return 0;
}
这段代码也很简单,为了说明伪句柄我们在主线程中创建了一个子线程,并且在调用子线程前让主线程休眠5秒钟,然后再把父线程的句柄传递给子线程,让子线程打印出父线程的创建时间。
若传递给子线程的句柄为父线程的,那么打印出的时间应该和主线程中打印的一样,以下是运行结果截图:
两次打印结果不同!嘿嘿。现在清楚了吧,我们在调用子线程前休眠了5秒,也就是说子线程的创建时间比父线程慢了5秒钟(打印出的就是子线程的创建时间!),我们前面说过无论在父线程还是子线程GetCurrenThread得到的值都为0xffffffe,那也就是说这个0xffffffe是线程相关的,即在哪个线程中就是指向该线程(进程的道理一样)
还有一点很重要的我们在主线程和子线程中都调用了CloseHandle并且让他们在关闭句柄是打印出close handle successful,但是结果却没有输出。也就是说:伪句柄本身不会打开内核对象的句柄表,因此内核对象的使用计数就不会增加所以在调用CloseHandle时仅仅是简单的返回(并没有真的关闭句柄)。
如果你想得到实际得句柄,在进程间进行通讯,必需要进行转化,需要调用DuplicateHandle,注意,得实句柄使用完成以后,你必须要调用CloseHandle去关闭.
其实,你应该明白了为何"伪句柄"得存在,就是使用简单,不用关闭,不会造成内存泄漏.
对上面那个程序做一点小改动:
#include <windows.h>
#include <stdio.h>
DWORD WINAPI ChildThread(LPVOID lpThreadparam)
{
HANDLE hChild=GetCurrentThread();
HANDLE hThreadParent = (HANDLE)lpThreadparam;
FILETIME ftCreationTime,ftExitTime,ftKernelTime,ftUserTime;
GetThreadTimes(hThreadParent,&ftCreationTime,&ftExitTime,&ftKernelTime,&ftUserTime);
/* 传进来的是真实的句柄 这里就不能提前把它关了
if(CloseHandle(hThreadParent))
{
printf("close handle2 successful\n");
} */
SYSTEMTIME systime1,systime2,systime3; //定义系统时间结构
FileTimeToSystemTime(&ftCreationTime, &systime1);//将文件时间 转换为 系统时间
printf("这个是在子线程里的~调用传来的参数显示父进程创建的时间:");
printf("%d 时%d分 %d秒\n",systime1.wHour,systime1.wMinute,systime1.wSecond);
return 0;
}
int main()
{
HANDLE hThread=GetCurrentProcess();
//HANDLE hParent=GetCurrentThread();
DWORD dwThreadId;
HANDLE hParent;
//**********************************
DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
&hParent,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
//hThreadParent = GetCurrentThread();
//**********************************
FILETIME ftCreationTime,ftExitTime,ftKernelTime,ftUserTime;
GetThreadTimes(hParent,&ftCreationTime,&ftExitTime,&ftKernelTime,&ftUserTime);
SYSTEMTIME systime1,systime2,systime3; //定义系统时间结构
FileTimeToSystemTime(&ftCreationTime, &systime1);//将文件时间 转换为 系统时间
printf("这个是在父线程里的~获得父线程创建时间:");
printf("%d 时%d分 %d秒\n",systime1.wHour,systime1.wMinute,systime1.wSecond);
Sleep(5000);
hThread=CreateThread(
NULL, // default security attributes
0, // use default stack size
ChildThread, // thread function
(LPVOID)hParent, // argument to thread function
0, // use default creation flags
&dwThreadId); // returns the thread identifier
system("pause");
if(CloseHandle(hParent)) //DuplicateHandle 增加了指定内核对象的使用计数 所以最后还得将他关了
{
printf("close handle1 successful\n");
}
return 0;
}
运行结果图:
这时两时间就一样了,就是说我们把真实的父句柄传给了子线程。