//jiachao Ren Ideas
是时候讨论句柄是什么了?当然真正清楚的是微软,我就是猜猜,呵呵。
不妥的说法:
1.句柄是一个指针,指向它标识的资源。
2.句柄是指向指针的指针…
这样的说法不如说句柄就是一个资源的标识更准确,因为句柄不是指针。
来看看:VC6 winnt.h头文件中的一段内容:
#ifdef STRICT
typedef void*HANDLE;
#define DECLARE_HANDLE(name) structname##__ { int unused; }; typedef struct name##__*name
#else
typedef PVOIDHANDLE;
#define DECLARE_HANDLE(name) typedefHANDLE name
#endif
typedef HANDLE*PHANDLE;
多么的让人联想到句柄是指针啊,呵呵,不要看表面。我在C语言课中讲过这个问题,这里重新再拿出来说说,这里含有一个非常牛的技巧,是的,就像STL的萃取机一样精彩。
现在环境是这样的,要定义一个数据类型,它需要满足几个基本要求:
1.可以标识一个资源。(当然整数很不错)
2.需要有层次结构,这个很重要。因为HINSTANCE就是HANDLE,HBITMAP(位图句柄),HDC(设备描述表句柄),HICON(图标句柄)等等,这些就是HANDLE。不理解的话,鸡、鸭、鹅这些都是家禽,一个道理。对,就像类继承一样,是ISA的关系。
好了越来越有眉目了。通过上述1,2我们排除了类对象,因为它不是一个简单是数字,它太高级在底层代码中并不适用。
于是我们选择了指针,这个好,因为我们都知道,int*也是void*,float*,char*都是void*(void*理解为一个单纯的指针,当然指向特定类型的指针ISA 指针)。
但是,如果这样,各种资源的标识就能互相转换了,很不安全。现在似乎想想可以理解了:
typedef void*HANDLE;
#define DECLARE_HANDLE(name) structname##__ { int unused; }; typedef struct name##__*name
那么HINSTANCE就是structHINSTANCE__*,HBITMAP就是struct HBITMAP__*,它们当然都是HANDLE,因为HANDLE是void*,都能转成HANDLE,且互相不可转换!
所以,不要认为句柄是指针,它是开发者利用一个技巧而实现的一个具有上述特性的“新类型”,就叫它句柄吧,它能唯一标识一个资源(别忘了我曾经说的,句柄是进程的,不多解释)。
好了,下面研究“伪句柄”和“真正的句柄”,在使用标识内核对象的句柄的时候,我们可能遇到这样的名词。
测试实例1:
//这两个宏可能并不好,但是最近要测试好多多线程的例子,这么写我能省去很多“体力”,呵呵。
#define ThreadProc(funName) DWORDWINAPI funName(LPVOID param)
#define ThreadProcEx(funName,paramName) DWORD WINAPI funName(LPVOID paramName)
void FileTimeShowSystemTime(constFILETIME *pft){
FILETIME localFileTime;
FileTimeToLocalFileTime(pft,&localFileTime);
SYSTEMTIME st;
FileTimeToSystemTime(&localFileTime,&st);
cout<<st.wHour<<":"<<st.wMinute<<":"<<st.wSecond;
}
ThreadProc(ChildThread);
ThreadProc(ParentThread){
HANDLE hParent = GetCurrentThread();
FILETIME creationTime, exitTime, kernelTime,userTime;
GetThreadTimes(hParent, &creationTime,&exitTime, &kernelTime,&userTime);
FileTimeShowSystemTime(&creationTime);
cout<<endl;
Sleep(5000);
HANDLE h = BEGINTHREADSIMPLE(ChildThread,hParent);
CloseHandle(h);
return 0;
}
ThreadProc(ChildThread){
FILETIME creationTime, exitTime, kernelTime,userTime;
GetThreadTimes((HANDLE)param, &creationTime,&exitTime, &kernelTime,&userTime);
FileTimeShowSystemTime(&creationTime);
cout<<endl;
return 0;
}
int main(int argc, char*argv[])
{
FILETIME creationTime, exitTime, kernelTime,userTime;
GetThreadTimes(GetCurrentThread(), &creationTime,&exitTime, &kernelTime,&userTime);
FileTimeShowSystemTime(&creationTime);
cout<<endl;
Sleep(5000);
HANDLE h = BEGINTHREADSIMPLE(ParentThread, NULL);
CloseHandle(h);
while(1){}
return 0;
}
程序结果:
14:50:51
14:50:56
14:51:1
很显然我们在ChildThread中没有得到ParentThread的CreateTime,这时就是伪句柄在作怪了:
注意:GetCurrentThread()、GetCurrentProcess() 所获得的句柄就是伪句柄,我们可以这么像,他们得到的不是句柄而是一个许可证,当亮出这个许可证的时候上下文环境就会认为这是一个标识着当前线程的句柄。所以无论我们把这个许可证传到哪里,得到的都是当前线程的句柄。在这个例子中就是ChildThread的句柄。
如何获得真实的句柄呢?也非常容易,见下面的例子:
//把ParentThread、ChildThread作如下修改:
ThreadProc(ParentThread){
HANDLE hParent;
DuplicateHandle(GetCurrentProcess(),GetCurrentThread(), GetCurrentProcess(), &hParent,0, FALSE, DUPLICATE_SAME_ACCESS);
FILETIME creationTime, exitTime, kernelTime,userTime;
GetThreadTimes(GetCurrentThread(), &creationTime,&exitTime, &kernelTime,&userTime);
FileTimeShowSystemTime(&creationTime);
cout<<endl;
Sleep(5000);
HANDLE h = BEGINTHREADSIMPLE(ChildThread,hParent);
CloseHandle(h);
return 0;
}
ThreadProc(ChildThread){
FILETIME creationTime, exitTime, kernelTime,userTime;
GetThreadTimes((HANDLE)param, &creationTime,&exitTime, &kernelTime,&userTime);
FileTimeShowSystemTime(&creationTime);
cout<<endl;
CloseHandle((HANDLE)param);//关闭拷贝出的真实的句柄。
return 0;
}
程序结果为:
15:25:26
15:25:31
15:25:31
符合逻辑
//当然DuplicateHandle调用起来也比较复杂可以利用一个简单的宏做一些常规调用时的简化,这样就可以方便的调用GetRealHandle(&hParent,GRH_THREADHANDLE); 当然如果需要设置句柄继承和权限的话还是需要调用DuplicateHandle传入一些相关参数。
#define GRH_PROCESSHANDLE0
#define GRH_THREADHANDLE1
#define GetRealHandle(pHandle, type)DuplicateHandle(
GetCurrentProcess(),
(type==GRH_PROCESSHANDLE)?GetCurrentProcess():((type==GRH_THREADHANDLE)?GetCurrentThread():INVALID_HANDLE_VALUE),
GetCurrentProcess(),
&hParent,
0,
FALSE,
DUPLICATE_SAME_ACCESS)
先到这里,希望对大家有帮助,By Jiachao Ren2011-7-6
附:
注意事项
内核对象句柄,是用来标识某个内核对象的一个ID同一个对象的该ID对于每个进程是不同的(句柄是进程的),具体如何实现是Microsoft不公开的算法,以下是一个近似的,可能的算法:
进程创建时,windows系统为进程构造了一个句柄表
当该进程希望获得一个内核对象句柄或者创建一个内核对象从而获得该对象句柄时
系统会将在句柄表中增加一个表项,表项的内容中存储了指向目标内核对象的指针
同时,系统返回这个表项在句柄表中的索引作为句柄(句柄不是指针,更可能是一个Index)。