WIN32下執行緒和視窗的資料繫結

zhoujianhei發表於2007-11-19
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。
 
五. 

相關文章