開源GUI-Microwindows之程式入口分析,microwindows
***************************************************************************************************************************
作者:EasyWave 時間:2014.10.05
類別:開源GUI系統-Microwindows之程式入口分析 聲明:轉載,請保留連結
注意:如有錯誤,歡迎指正。這些是我學習的日誌文章......
***************************************************************************************************************************
一:MicroWindows程式入口
這裡只分析基於WIN32 Message方式部分,對於Nano不是這裡的分析重點,相信熟悉Linux核心的,應該都知道在Linux下,不管是裝置,驅動,還是進程,都是採用鏈表的方式將各個宿主要資料結構連結起來,而在Microwindows中也採用內似的方式,我們先來複習下Linux的雙向鏈表吧,在Linux核心中,有大量的資料結構需要用到雙迴圈鏈表,例如進程、檔案、模組、頁面等。若採用雙迴圈鏈表的傳統實現方式,需要為這些資料結構維護各自的鏈表,並且為每個鏈表都要設計插入、刪除等操作函數。因為用來維持鏈表的next和prev指標指向對應類型的對象,因此一種資料結構的鏈表操作函數不能用於操作其它資料結構的鏈表。
在Linux原始碼樹的include/linux/list.h檔案中,採用了一種類型無關的雙迴圈鏈表實現方式。其思想是將指標prev和next從具體的資料結構中提取出來構成一種通用的"雙鏈表"資料結構list_head。如果需要構造某類對象的特定鏈表,則在其結構(被稱為宿主要資料結構)中定義一個類型為list_head類型的成員,通過這個成員將這類對象串連起來,形成所需鏈表,並通過通用鏈表函數對其進行操作。其優點是只需編寫通用鏈表函數,即可構造和操作不同對象的鏈表,而無需為每類對象的每種列表編寫專用函數,實現了代碼的重用。如下所示的定義:
struct list_head {
struct list_head *next, *prev;
};
在Linux核心中的雙迴圈鏈表實現方式如下:
- list_head類型的變數作為一個成員嵌入到宿主要資料結構內;
- 可以將鏈表結構放在宿主結構內的任何地方;
- 可以為鏈表結構取任何名字;
- 宿主結構可以有多個鏈表結構;
- 用list_head中的成員和相對應的處理函數來對鏈表進行遍曆;
- 如果想得到宿主結構的指標,使用list_entry可以算出來。
Linux中是如何通過list_head擷取宿主要資料結構的呢?這裡嘗試分析一下: 如果需要有某種資料結構的隊列,就在這種資料結構定義內部放上一個list_head資料結構。例如,建立資料結構foo鏈表的方式是,在foo的定義中,嵌入了一個list_head成員list。這裡就是所指的"宿主"。
typedef struct dtest {
…
struct list_head list;
…
};
但是,如何通過list_head成員訪問到宿主結構項呢?畢竟list_head不過是個串連件,而我們需要的是一個"特定"的資料結構鏈表。
先介紹幾個基本宏:offsetof、typeof、containerof
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
而__builtin_offsetof()宏就是在編譯器中已經設計好了的函數,直接調用即可。
#undef offsetof //取消先前的任何定義,可以保證下面的定義生效
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
上面的演算法一共分下面幾步:
- ( (TYPE *)0 ) 0地址強制 "轉換" 為 TYPE結構的指標;
- ((TYPE *)0)->MEMBER 訪問結構中的資料成員;
- &( ( (TYPE *)0 )->MEMBER)取出資料成員的地址
- (size_t)(&(((TYPE*)0)->MEMBER))結果轉換類型.
巧妙之處在於將0轉換成(TYPE*),結構以記憶體空間首地址0作為起始地址,則成員地址自然為位移地址; 這裡使用的是一個利用編譯器技術的小技巧(編譯器自動算出成員的位移量),即先求得結構成員變數在結構體中的相對於結構體的首地址的位移地址,然後根據結構體的首地址為0,從而得出該位移地址就是該結構體變數在該結構體中的位移,即:該結構體成員變數距離結構體首的距離。在offsetof()中,這個member成員的地址實際上就是type資料結構中member成員相對於結構變數的位移量。對於給定一個結構,offsetof(type,member)是一個常量,list_entry()正是利用這個不變的位移量來求得鏈表資料項目的變數地址。
ontainer_of - cast a member of a structure out to the containing structure。
ptr: the pointer to the member.
type: the type of the container struct this is embedded in.
member: the name of the member within the struct.
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
list_entry()宏,擷取當前list_head鏈表節點所在的宿主結構項。第一個參數為當前list_head節點的指標,即指向宿主結構項的list_head成員。第二個參數是宿主要資料結構的定義類型。第三個參數為宿主結構類型定義中list_head成員名。
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
擴充替換即為:
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
例如,我們要訪問dtest鏈表(鏈表頭為head)中首個元素,則如此調用:
list_entry(head->next, struct dtest, list);
經過C預先處理的文字替換,這一行的內容就成為:
((struct dtest *)((char *)(head->next) - (unsigned long)(&((struct dtest *)0)->list)))
考慮list_head類型成員member相對於宿主結構(類型為type)起始地址的位移量。對於所有該類型的宿主對象,這個位移量是固定的。並且可以在假設宿主對象地址值為0,通過返回member成員的地址獲得,即等於(unsigned long)(&((type *)0)->member)。這樣,將當前宿主對象的"串連件"地址(ptr)減去這個位移量,得到宿主對象地址,再將它轉換為宿主要資料結構類型的指標。 需要重申的是,鏈表頭必須被嵌入到宿主對象中。
而在MicroWindows用也採用了內似的方法將各個WNDCLASS連結起來,並且通過MwFindClassByName(lpWndClass->lpszClassName);函數尋找對應的已經註冊過的WNDCLASS,現在來分析下MircoWindows的初始化程式吧,源碼是在Winmain.c中,如下所示:
在中的0064行的MwOpen(ac, av)函數是這裡重點分析的代碼,而mwCreateInstance()函數是建立應用程式的控制代碼,但是它是相對於rootwp而言的,也就是Desktop,就是大家常說的案頭。而WinMain()就是我們需要實現的應用程式GUI,這個就是我們需要發揮的地方,目前這裡先分析MwOpen函吧,如下所示:
在MwOpen函數中,通過調用MwInitialize函數,而MwInitialize函數的詳情如下:
這裡主要分析這行代碼:wp->pClass = (PWNDCLASS)mwClassHead.head;,而mwClassHead的定義如下所示:
也就是說,在MicroWindows中也採用Linux中內似的鏈表來實現管理的,而mwClassHead有誰在使用呢,答案是RegisterClass(CONST WNDCLASS *lpWndClass)函數,如下所示:
RegisterClass(CONST WNDCLASS *lpWndClass)函數中會去調用MwFindClassByName(LPCSTR lpClassName),而在MwFindClassByName(LPCSTR lpClassName)函數中會去調用GdItemAddr,是否有似曾相識的感覺呢,不過,我們可以通過解析如下:
((WNDCLASS*)((long)mwClassHead.head - MWTEM_OFFSET(WNDCLASS, link)))進一步的解析如下:((WNDCLASS*)( (long)mwClassHead.head - ((long)&(((WNDCLASS *)0)->link)) )
通過這樣的方式就可以尋找到之前是否已經相同的控制項註冊到WNDCLASS 系統中去啦。跟Linux核心的鏈表有異曲同工之妙哦。而WNDCLASS的定義在下面,通過定義可以看到在WNDCLASS中有一個link的成員變數,如下所示:
從可以看到在tagWNDCLASSA結構體中有MWLIST link。
二:總結 Microwindows也採用了一種類型無關的雙迴圈鏈表實現方式。其思想是將指標head和tail從具體的資料結構中提取出來構成一種通用的"雙鏈表"資料結構MWLISTHEAD。如果需要構造某類對象的特定鏈表,則在其結構(被稱為宿主要資料結構)中定義一個類型為MWLISTHEAD類型的成員,通過這個成員將這類對象串連起來,形成所需鏈表,並通過通用鏈表函數對其進行操作。其優點是只需編寫通用鏈表函數,即可構造和操作不同對象的鏈表,而無需為每類對象的每種列表編寫專用函數,實現了代碼的重用。
網站開來源程式
pageadmin cms、帝國cms、wordpress cms等等。
免費開源網站程式
開源的一般都要求留著作權。更多使用者會把它刪掉。你也可以一樣。asp的5ucms不錯。著作權刪掉就可以了。