//jiachao Ren Ideas
是時候討論句柄是什么了?當然真正清楚的是微軟,我就是猜猜,呵呵。
不妥的說法:
1.句柄是一個指針,指向它標識的資源。
2.句柄是指向指針的指針…
這樣的說法不如說句柄就是一個資源的標識更準確,因為句柄不是指針。
來看看:VC6 winnt.h頭文件中的一段內(nèi)容:
#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;
多么的讓人聯(lián)想到句柄是指針啊,呵呵,不要看表面。我在C語言課中講過這個問題,這里重新再拿出來說說,這里含有一個非常牛的技巧,是的,就像STL的萃取機一樣精彩。
現(xiàn)在環(huán)境是這樣的,要定義一個數(shù)據(jù)類型,它需要滿足幾個基本要求:
1.可以標識一個資源。(當然整數(shù)很不錯)
2.需要有層次結(jié)構(gòu),這個很重要。因為HINSTANCE就是HANDLE,HBITMAP(位圖句柄),HDC(設備描述表句柄),HICON(圖標句柄)等等,這些就是HANDLE。不理解的話,雞、鴨、鵝這些都是家禽,一個道理。對,就像類繼承一樣,是ISA的關(guān)系。
好了越來越有眉目了。通過上述1,2我們排除了類對象,因為它不是一個簡單是數(shù)字,它太高級在底層代碼中并不適用。
于是我們選擇了指針,這個好,因為我們都知道,int*也是void*,float*,char*都是void*(void*理解為一個單純的指針,當然指向特定類型的指針I(yè)SA 指針)。
但是,如果這樣,各種資源的標識就能互相轉(zhuǎn)換了,很不安全?,F(xiàn)在似乎想想可以理解了:
typedef void*HANDLE;
#define DECLARE_HANDLE(name) structname##__ { int unused; }; typedef struct name##__*name
那么HINSTANCE就是structHINSTANCE__*,HBITMAP就是struct HBITMAP__*,它們當然都是HANDLE,因為HANDLE是void*,都能轉(zhuǎn)成HANDLE,且互相不可轉(zhuǎn)換!
所以,不要認為句柄是指針,它是開發(fā)者利用一個技巧而實現(xiàn)的一個具有上述特性的“新類型”,就叫它句柄吧,它能唯一標識一個資源(別忘了我曾經(jīng)說的,句柄是進程的,不多解釋)。
好了,下面研究“偽句柄”和“真正的句柄”,在使用標識內(nèi)核對象的句柄的時候,我們可能遇到這樣的名詞。
測試實例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;
}
程序結(jié)果:
14:50:51
14:50:56
14:51:1
很顯然我們在ChildThread中沒有得到ParentThread的CreateTime,這時就是偽句柄在作怪了:
注意:GetCurrentThread()、GetCurrentProcess() 所獲得的句柄就是偽句柄,我們可以這么像,他們得到的不是句柄而是一個許可證,當亮出這個許可證的時候上下文環(huán)境就會認為這是一個標識著當前線程的句柄。所以無論我們把這個許可證傳到哪里,得到的都是當前線程的句柄。在這個例子中就是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);//關(guān)閉拷貝出的真實的句柄。
return 0;
}
程序結(jié)果為:
15:25:26
15:25:31
15:25:31
符合邏輯
//當然DuplicateHandle調(diào)用起來也比較復雜可以利用一個簡單的宏做一些常規(guī)調(diào)用時的簡化,這樣就可以方便的調(diào)用GetRealHandle(&hParent,GRH_THREADHANDLE); 當然如果需要設置句柄繼承和權(quán)限的話還是需要調(diào)用DuplicateHandle傳入一些相關(guān)參數(shù)。
#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
附:
注意事項
內(nèi)核對象句柄,是用來標識某個內(nèi)核對象的一個ID同一個對象的該ID對于每個進程是不同的(句柄是進程的),具體如何實現(xiàn)是Microsoft不公開的算法,以下是一個近似的,可能的算法:
進程創(chuàng)建時,windows系統(tǒng)為進程構(gòu)造了一個句柄表
當該進程希望獲得一個內(nèi)核對象句柄或者創(chuàng)建一個內(nèi)核對象從而獲得該對象句柄時
系統(tǒng)會將在句柄表中增加一個表項,表項的內(nèi)容中存儲了指向目標內(nèi)核對象的指針
同時,系統(tǒng)返回這個表項在句柄表中的索引作為句柄(句柄不是指針,更可能是一個Index)。
愛華網(wǎng)



