來自 乞丐中的霸主 http://blog.csdn.net/zhoujianhei/archive/2007/11/19/1891891.aspx
WIN32下線程和視窗的資料繫結
一.
寫給自己我很懶。這是我寫的第一篇涉及到編程的文章,也許就是因為太懶的緣故吧。在此之前也有寫的衝動可不知為什麼沒有寫成,懶得回憶了。說道記憶,啊,去年我在陳家莊。。。暈,又來了。有些事情我確實懶得記啦,以至於過去苦心鑽研的“經--典”代碼,現在又得重新研究。後悔當初沒有給自己留下點什麼痕迹,蛛絲馬跡也好啊。本來很懶的我現在又得做重複勞動啊,那麼我到底懶不懶啊?唉,總得給自己留下點什麼吧,哪怕回憶也好啊。由於本人記性不好,所以留此文章,已被後查。
二.
問題的提出最近想在WIN32下對視窗封裝一下。於是查閱了很多資料並分析了MFC的視窗實現,這才知道MFC的博大(有夠大)。同時也使我積累了很多知識和技術,其實經常分析MFC實現對程式的編寫和設計是大有益處,高手就在於你看了多少代碼。這裡不對MFC做過多評價,我們需要的僅是精華部分。
三.
線程資料的綁定有的時候我們需要將一些資料或控制代碼綁定到當前線程,以供不時只需。在WIN32編程中,有些系統回呼函數並沒有準備足夠的參數為我們傳遞資料。而在這些回呼函數中就包括WindowProc,TimerProc等。下面介紹一種方法將我們的資料繫結到系統的當前線程中,線上程中的任何一個角落都會取到我們之前繫結資料,線程到哪我們的資料也就到哪。接下來我們所使用的技術稱作執行緒區域儲存(TLS)。
執行緒區域儲存要將資料(指標、控制代碼)綁定到線程,我們需要一個全域索引,而這個索引正是通過TlsAlloc分配的。TlsAlloc函數功能:分配一個線程局部儲存(TLS)索引。該進程的任何線程都可以使用該索引來儲存和檢取線程中的值。函數原型:DWORD TlsAlloc(void)參數:無。傳回值:若函數成功,則傳回值為一個TLS索引。失敗則返回0XFFFFFFFF。需要說明的是,進程可以同時分配多個索引,用於存放不同的資料。下面提供進程、線程、索引、儲存槽的關係圖以便更好地理解。
當越過進程邊界時,TLS索引變為無效。一個DLL不能假定在一個進程中分配的索引在另一個進程中依然有效。當一個DLL附加到一個進程時,它使用TlsAlloc分配一個TLS索引。然後,DLL分配一些動態儲存裝置單元,並調用TlsSetValue向TLS槽中儲存地址。TLS索引儲存在DLL的全域或靜態變數中。有了這個索引,我們就可以通過它來取得、設定資料,然而這些資料只對當前線程可見。針對索引系統為每個線程分配一個儲存槽,當然我們可以隨意的來使用這個槽。下面的函數用於取和設槽置。TlsGetValue函數功能:檢取調用線程的線程局部儲存(TLS)槽的值。對於每個TLS索引,進程的每個線程都有它自己的槽。函數原型:LPVOID TlsGetValue(DWORD dwTlsIndex)參數:dwTlsIndex:由TlsAlloc分配的索引。傳回值:若函數成功,則返回調用線程的TLS槽中的值;失敗則返回0。注意,存放在TLS槽中值可以為0,在這種情況下GetLastError返回NO_ERROR。每個線程的TLS槽被初始化為NULL。TlsSetValue函數功能:儲存調用線程的線程局部儲存(TLS)槽的值。函數原型:BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue)參數:dwTlsIndex:由TlsAlloc分配的索引。lpTlsValue:調用線程的線程局部儲存(TLS)槽的值。傳回值:若函數成功,則傳回值不為0;失敗則返回0。TlsSetValue和TlsGetValue實現以提高速度為目標。這些函數執行最小的參數驗證和錯誤檢查。 當資料不再有用,我們需要將索引釋放,有始有終嗎。TlsFree函數不釋放任何與TLS相關的動態儲存裝置單元。TlsFree函數功能:釋放調用線程局部儲存(TLS)索引。函數原型:BOOL TlsFree(DWORD dwTlsIndex)參數:dwTlsIndex:由TlsAlloc分配的索引。傳回值:若函數成功,則傳回值不為0;失敗則返回0。
四.
視窗資料的綁定下面介紹視窗資料的綁定,如果在WIN32下經常建立視窗,那麼你一定對WindowProc函數不會陌生了,所有的視窗訊息都是通過它來分配到視窗的。WindowProc無非就是windows的一個回呼函數,下面對該函數做下說明:WindowProc函數功能:該函數是一個應用程式定義的函數。它處理髮送給視窗的訊息。WNDPROC類型定義了一個指向該函數的指標。函數原型:LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)參數:hWnd:指向視窗的控制代碼。uMsg:指定訊息。wParam, lParam:指定uMsg訊息的特定資訊。傳回值:訊息的處理結果,它與發送的訊息有關。所有的回呼函數都是全域或靜態,然而根據上面的函數描述並沒有給我們留下什麼儲存空間的指標,那麼我們的資料如何綁定到視窗上呢? SetWindowLong函數功能:改變指定視窗的屬性,也將指定的一個32位值設定在視窗的額外儲存空間中。函數原型:LONG SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong)參數:hWnd:視窗控制代碼。nIndex:指定大於0的位移值。可以指定下面的值之一:GWL_EXISTYLE:設定一個新的擴充風格。GWL_STYLE:設定一個新的視窗風格。GWL_WNDPROC:為視窗過程設定一個新的地址。GWL_ID:設定一個新的視窗標識符。GWL_HINSTANCE:設定一個新的應用程式執行個體控制代碼。GWL_USERDATA:設定與視窗有關的32位值。每個視窗均有一個由建立該視窗的應用程式使用的32位值。當hWnd參數標識了一個對話方塊時,也可以使用下列值:DWL_DLGPROC:設定對話方塊過程的新地址。DWL_MSGRESULT:設定對話方塊過程中處理的訊息的傳回值。DWL_USER:設定應用程式私人的額外資訊,例如一個控制代碼或指標。dwNewLong:指定的替換值。傳回值:若成功返回原來的32位值,失敗則返回0。 GetWindowLong函數功能:擷取指定視窗的屬性,也將擷取視窗的額外儲存空間中的一個32位值。函數原型:LONG GetWindowLong(HWND hWnd, int nIndex)參數:hWnd:視窗控制代碼。nIndex:指定大於0的位移值。可以指定下面的值之一:GWL_EXISTYLE:擷取擴充風格。GWL_STYLE:擷取視窗風格。GWL_WNDPROC:擷取視窗過程地址。GWL_ID:擷取視窗標識符。GWL_HINSTANCE:擷取應用程式執行個體控制代碼。GWL_USERDATA:擷取與視窗有關的32位值。每個視窗均有一個由建立該視窗的應用程式使用的32位值。當hWnd參數標識了一個對話方塊時,也可以使用下列值:DWL_DLGPROC:擷取對話方塊過程地址。DWL_MSGRESULT:擷取對話方塊過程中一個處理的訊息的傳回值。DWL_USER:擷取應用程式私人的額外資訊,例如一個控制代碼或指標。傳回值:若成功返回原來的32位值,失敗則返回0。