thunk技術實現視窗類的封裝

2021-04-21 01:06:35 字數 2801 閱讀 8276

當前位置:文章中心 →

技術開發 →

程式開發 → 文章內容

減小字型

增大字型

mfc功能已經非常強大,自己做介面庫也許沒什麼意思,但是這個過程中卻能學到很多東西。比如說:

視窗類的封裝,從全域性視窗訊息處理到視窗物件訊息處理的對映方法:

對介面進行封裝,一般都是乙個視窗乙個類,比如實現乙個最基本的視窗類cmywnd,你一定會把視窗過程作為這個類的成員函式,但是使用winapi建立 視窗時必須註冊類wndclass,裡面有個成員資料lpfnwndproc需要wndproc的函式指標,一般想法就是把視窗類的訊息處理函式指標傳過 去,但是類成員函式除非是靜態的,否則無法轉換到wndproc,而全域性的訊息處理函式又無法得到視窗類物件的指標。這裡有幾種解決辦法:

一種解決方法是用視窗列表,開乙個結構陣列,視窗類物件建立視窗的時候把視窗hwnd和this指標放入陣列,全域性訊息處理函式遍歷陣列,利用hwnd找出this指標,然後定位到物件內部的訊息處理函式。這種方法查詢物件的時間會隨著視窗個數的增多而增長。

另一種方法比較聰明一點,wndclass裡面有個成員資料cbwndextra一般是不用的,利用這點,註冊類時給該成員資料賦值,這樣視窗建立時系統 會根據該值開闢一塊記憶體與視窗繫結,這時把建立的視窗類的指標放到該塊記憶體,那麼在靜態的視窗訊息迴圈函式就能利用getwindowlong (hwnd,gwl_userdata)取出該指標,return (cmywnd*)->windowproc(...),這樣就不用遍歷視窗了。但是這樣一來就有個致命弱點,對視窗不能呼叫 setwindowlong(hwnd,gwl_userdata,資料),否則就會導致程式崩潰。幸好這個函式(特定這幾個引數)是呼叫機率極低的,對 於視窗,由於建立視窗都是呼叫視窗類的create函式,不用手工註冊wndclass類,也就不會呼叫setwindowlong函式。但是畢竟缺乏安 全性,而且當一秒鐘內處理的視窗訊息很多時,這種查詢速度也可能不夠快。

還有一種就是比較完美的解決辦法,稱之為thunk技 術。thunk是一組動態生成的asm指令,它記錄了視窗類物件的this指標,並且這組指令可以當作函式,既也可以是視窗過程來使用。thunk先把窗 口物件this指標記錄下來,然後轉向到靜態stdproc**函式,轉向之前先記錄hwnd,然後把堆疊裡hwnd的內容替換為this指標,這樣在 stdproc裡就可以從hwnd取回物件指標,定位到windowproc了。

我們先來看看視窗過程函式定義:

lresult winapi windowproc(hwnd hwnd,uint umsg,wparam wparam,lparam lparam)

其實當我們的視窗類cmywnd建立視窗的時候,視窗控制代碼是可以得到並且作為成員資料儲存,如此一來,第乙個引數hwnd是可以不要的,因為可以通過 this->m_hwnd得到,我們可以在這裡做手腳,hwnd其實質是乙個指標,如果把這個引數替換為視窗類物件的this指標,那麼我們不就可 以通過(cmywnd*)hwnd->windowproc轉到視窗類內部的視窗過程了嗎?但是視窗過程是系統呼叫的,怎麼能把hwnd替換掉呢? 我們先來看看系統呼叫這個函式時的堆疊情況:

系統呼叫m_thunk時的堆疊:

ret hwnd msg wparam lparam

-------------------------------------------

棧頂                                              棧底

#pragma pack(push,1) //該結構必須以位元組對齊

struct thunk ;

#pragma pack(pop)

類定義:

class cmywnd

...

private:

wndproc m_thunk

}在建立視窗的時候把視窗過程設定為this->m_thunk,m_thunk的型別是wndproc,因此是完全合法的,當然這個m_thunk還沒有初始化,在建立視窗前必須初始化:

wndproc cmywnd::createthunk()

這樣m_thunk雖然是乙個結構,但其資料是一段可執行的**,而其型別又是wndproc,系統就會忠實地按視窗過程規則呼叫這段**,m_thunk就把window欄位裡記錄的this指標替換掉堆疊中的hwnd引數,然後跳轉到靜態的stdproc:

//本**函式的hwnd呼叫之前已由m_thunk替換為物件指標

lresult winapi cmywnd::stdproc(hwnd hwnd,uint umsg,uint wparam,long lparam)

這樣就把視窗過程轉向到了類成員函式windowproc,當然這樣還有乙個問題,就是視窗控制代碼hwnd還沒來得及記錄,因此一開始的視窗過程應該先定位到靜態的initproc,createwindow的時候給最後乙個引數,即初始化引數賦值為this指標:

createwindowex(

dwexstyle,

szclass,

sztitle,

dwstyle,

x,y,

width,

height,

hparentwnd,

hmenu,

hinst,

this            //初始化引數

);,在initproc裡面取出該指標:

Thunk 技術的乙個改進

thunk 技術的乙個改進 摘要 介紹了 thunk 技術中如何避免直接寫機器碼。關鍵字 thunk 機器碼 this指標 thunk技術,一般認為是在程式中直接構造出可執行 的技術 在正常情況下,這是編譯器的任務 深度探索c 物件模型 中對這個詞的 有過考證 在中文版的162頁 說thunk是kn...

Thunk 技術的乙個改進

thunk 技術的乙個改進 摘要 介紹了 thunk 技術中如何避免直接寫機器碼。關鍵字 thunk 機器碼 this指標 thunk技術,一般認為是在程式中直接構造出可執行 的技術 在正常情況下,這是編譯器的任務 深度探索c 物件模型 中對這個詞的 有過考證 在中文版的162頁 說thunk是kn...

ADO封裝類的實現

宣告 用來訪問資料庫的類。要用次類的話,程式中應該進行的初始化工作為 在stdafx.h中包含下面一句,用來將ado的dll檔案引入到程式中 import c program files mon files system ado msado15.dll no namespace rename eof...