在使用很多函数的时候,我们都需要获得一个对象的句柄,而某些函数返回的是伪句柄。伪句柄本身不会打开内核对象的句柄表,因此内核对象的使用计数就不会增加。它本身就只指向调用它的主调进程或线程。会因为调用者的不同而改变,比如:调用者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; }
运行结果图:
这时两时间就一样了,就是说我们把真实的父句柄传给了子线程。