现在的位置: 首页 > 综合 > 正文

Windows 伪句柄 详解

2018年06月06日 ⁄ 综合 ⁄ 共 4465字 ⁄ 字号 评论关闭

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

运行结果图:



这时两时间就一样了,就是说我们把真实的父句柄传给了子线程。

抱歉!评论已关闭.