<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    treenode

    在路上。

    BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
      5 Posts :: 1 Stories :: 53 Comments :: 0 Trackbacks

    前言

    ?
    作為一個(gè)專(zhuān)注于
    C/S 方面開(kāi)發(fā)的程序員,我一直對(duì)“面向?qū)ο蟮木幊炭蚣苋绾闻c Windows 操作系統(tǒng)的消息機(jī)制打交道”這個(gè)問(wèn)題有著相當(dāng)大的興趣。讀者想必知道,象 MFC VCL SWT 這樣的類(lèi)庫(kù)在實(shí)現(xiàn)界面處理的時(shí)候,有幾個(gè)主要問(wèn)題是不得不考慮的。首先是如何為窗口和控件這樣的界面以面向?qū)ο蠓绞竭M(jìn)行包裝——這一方面可以說(shuō)沒(méi)多少技術(shù)上的難題;從一般意義上講,不過(guò)是把 HWND 作為第一個(gè)參數(shù)的函數(shù)分類(lèi)整理一下而已。當(dāng)然,具體作起來(lái)還是有不少東西需要認(rèn)真考慮,只是這些問(wèn)題多半是在設(shè)計(jì)的層面,考慮包裝是否完善、維護(hù)和擴(kuò)展起來(lái)是否方便等等;在實(shí)現(xiàn)上基本上就沒(méi)什么需要克服的技術(shù)障礙了。而另一方面——即如何處理系統(tǒng)消息機(jī)制,則是一個(gè)頗費(fèi)腦筋的問(wèn)題了。其中最大的難點(diǎn)之一,就是 Windows 的消息系統(tǒng)依賴于窗口過(guò)程(術(shù)語(yǔ)叫做 Window Procedure ),而這個(gè)窗口過(guò)程卻是一個(gè)非面向?qū)ο蟮摹⑵胀ǖ娜趾瘮?shù),它完全不理解對(duì)象是什么;而為了讓整個(gè)程序 OO 起來(lái),你還非得讓它去操縱對(duì)象不可。因此,如何將窗口過(guò)程用面向?qū)ο蟮姆椒ㄍ昝赖姆庋b起來(lái),就成為各種類(lèi)庫(kù)面臨的最大挑戰(zhàn)之一。當(dāng)然,這也理所當(dāng)然的成為各個(gè)開(kāi)發(fā)小組展示自身功力的絕好舞臺(tái)。

    ?
    據(jù)我所知,在此一問(wèn)題上,不同的類(lèi)庫(kù)采納了不同的做法。較早的
    MFC 使用了窗口查找表的技術(shù),即為每個(gè)窗口和對(duì)應(yīng)的窗口過(guò)程建立一個(gè)映射;需要處理消息的時(shí)候,則是映射表中找到窗口所對(duì)應(yīng)的過(guò)程,并調(diào)用之。這樣會(huì)帶來(lái)幾個(gè)問(wèn)題。首先是每次進(jìn)行查表勢(shì)必浪費(fèi)時(shí)間,為此 MFC 不惜在關(guān)鍵處使用 Cache 映射和內(nèi)聯(lián)匯編的方法以提高效率。第二個(gè)問(wèn)題:映射表是和線程相關(guān)聯(lián)的,如果你將窗口傳遞給另外一個(gè)線程, MFC 無(wú)法在該線程中找到窗口的映射項(xiàng),也就不知該如何是好,于是只能出錯(cuò)。我已經(jīng)在很多地方看到有人問(wèn)跨線程傳遞窗口指針的疑問(wèn),多半都是因?yàn)椴焕斫?/span> MFC 的消息處理機(jī)制。正因?yàn)槿绱耍?/span> MFC 的使用者必須強(qiáng)制遵守一些調(diào)用方面的約定,否則會(huì)出現(xiàn)很多莫名其妙的錯(cuò)誤,這無(wú)疑是框架不夠友好的表現(xiàn)。而稍晚出現(xiàn)的 VCL ATL 則使用了一種比較巧妙的 Thunk 技術(shù),利用函數(shù)調(diào)用過(guò)程中使用堆棧的原理,巧妙的將對(duì)象指針“暗度陳倉(cāng)”地偷偷傳遞進(jìn)去,并通過(guò)一些內(nèi)存中的“小動(dòng)作”越過(guò)了通常的處理機(jī)制。這樣做的好處是節(jié)省了額外維護(hù)映射表的開(kāi)銷(xiāo),速度相當(dāng)快,同時(shí)也不存在線程傳遞的問(wèn)題。當(dāng)然,這個(gè)過(guò)程因?yàn)榇罅渴褂脜R編,而且需要對(duì)函數(shù)調(diào)用的底層機(jī)制有深刻的理解,所以很難為一般程序員所理解和運(yùn)用。(相應(yīng)的維護(hù)起來(lái)也難度也比較高——還記得 Anders 離開(kāi) Borland 以后相當(dāng)長(zhǎng)時(shí)間沒(méi)有人敢改動(dòng) Delphi 底層代碼的往事嗎?)

    ?

    在眾多框架中, SWT 算是比較年輕的一個(gè),也是頗為獨(dú)特的一個(gè)。之所以說(shuō)它特殊,因?yàn)樗怯?/span> Java 編寫(xiě)的。我們知道,和 Windows 平臺(tái)上的本地開(kāi)發(fā)工具不同, Java 程序是生活在自己的虛擬機(jī)中的,除非通過(guò) JNI 這個(gè)后門(mén),否則它對(duì)底下的操作系統(tǒng)根本一無(wú)所知。這顯然為設(shè)計(jì)者提出了更高的挑戰(zhàn)。那么, SWT 又是如何實(shí)現(xiàn)這一點(diǎn)的呢?非常幸運(yùn), SWT 是完全開(kāi)放源代碼的(當(dāng)然, MFC VCL 也是開(kāi)放的,不過(guò)這種開(kāi)放就比較小家子氣——許多時(shí)候只有你購(gòu)買(mǎi)昂貴的企業(yè)版以后才能看到這些寶貴的源碼, D 版且不論)。開(kāi)放源代碼為我們研究其實(shí)現(xiàn)掃清了障礙。

    ?

    準(zhǔn)備工作

    ?

    在上路之前,我們應(yīng)當(dāng)準(zhǔn)備好足夠的武器。當(dāng)然, Eclipse 是必不可少的——我使用的是最新的 Eclipse 3.2 RC6 版本,不過(guò)只要是 3.x 的版本,在核心代碼方面應(yīng)該不會(huì)有很大差別,所以對(duì)本文的目的而言, Eclipse 3.0 以上的任何版本都是夠用的。此外,如果你還沒(méi)有安裝任何界面開(kāi)發(fā)方面的插件的話,我強(qiáng)烈建議你安裝一個(gè) Eclipse.org 官方的 Visual Editor 。這倒不是說(shuō)我認(rèn)為該插件對(duì)界面開(kāi)發(fā)有多大的助力——事實(shí)上從功能上來(lái)說(shuō)它要比 SWT Designer 等同類(lèi)產(chǎn)品遜色;但是該插件最大的好處在于可以非常簡(jiǎn)單的設(shè)定好 SWT 程序所運(yùn)行的環(huán)境,還包括源代碼支持,這樣你就可以很輕松的跟蹤到 SWT 源代碼內(nèi)部去了。并且這個(gè)工具是沒(méi)有使用限制的,也不需要注冊(cè)激活,這一點(diǎn)要比 SWT Designer 來(lái)得方便。

    ?

    安裝 Visual Editor 以后,你可以在創(chuàng)建項(xiàng)目的過(guò)程中使用 Java Settings 頁(yè)面,或者在項(xiàng)目創(chuàng)建以后再選擇項(xiàng)目屬性,從 Java Build Path 分支下的 Libraries 頁(yè)面訪問(wèn)同樣的界面:

    然后按下 Add Library 按鈕。如果 Visual Editor 安裝正確,這里會(huì)多出一個(gè) Standard Widget Toolkit 項(xiàng)。選擇它然后 Next


    默認(rèn)選中的 IDE Platform 不用變,不過(guò)最好也勾選上 Include support for JFace library



    然后按 Finish 。這樣準(zhǔn)備工作就完成了。

    ?

    ?上路吧!

    ?

    現(xiàn)在我們可以對(duì) SWT 的源代碼著手進(jìn)行分析了。不過(guò),應(yīng)當(dāng)從哪里開(kāi)始下手呢?答案取決于對(duì)消息機(jī)制的理解。我們知道,任何 Windows 程序(嚴(yán)格地說(shuō),應(yīng)當(dāng)是有用戶界面的程序,而不包括控制臺(tái)應(yīng)用和系統(tǒng)服務(wù)程序)都是從 WinMain 開(kāi)始的;而 WinMain 中最重要的部分則是消息循環(huán),這也是任何 Windows 程序得以持續(xù)運(yùn)行的生命之源,所以有人稱(chēng)之為“消息泵”,就是因?yàn)樗笮呐K一樣為應(yīng)用程序的生命源源不斷的輸送動(dòng)力。通常,在用 SDK 編寫(xiě)的程序中會(huì)有如下的調(diào)用:

    while ?(?GetMessage( & msg,?NULL,? 0 ,? 0 )?)

    {

    ???TranslateMessage(?
    & msg?);

    ???DispatchMessage(?
    & msg?);
    }

    ?
    SWT 應(yīng)用程序,盡管實(shí)現(xiàn)方法不同,但是看起來(lái)非常相似:

    while ?(? ! shell.isDisposed()?)

    {

    ????
    if ?(? ! display.readAndDispatch()?)

    ???????display.sleep();

    }

    僅從文字上推斷,也很容易猜想:Display.readAndDispatch()方法所作的和SDK程序中Translate/Dispatch兩行所作的事情應(yīng)該是類(lèi)似的;而sleep方法,則在SDK程序中沒(méi)有直接的對(duì)應(yīng)物。接下來(lái),我們可以按住Ctrl鍵然后點(diǎn)擊readAndDispatch方法,去探查一下它內(nèi)部是如何實(shí)現(xiàn)的。

    public ? boolean ?readAndDispatch?()?{

    ????checkDevice?();

    ????drawMenuBars?();

    ????runPopups?();

    ????
    if ?(OS.PeekMessage?(msg,? 0 ,? 0 ,? 0 ,?OS.PM_REMOVE))?{

    ???????
    if ?( ! filterMessage?(msg))?{

    ???????????OS.TranslateMessage?(msg);

    ???????????OS.DispatchMessage?(msg);

    ???????}

    ???????runDeferredEvents?();

    ???????
    return ? true ;

    ????}

    ????
    return ?runMessages? && ?runAsyncMessages?( false );


    雖然這里有一些新鮮的東西,不過(guò)總體上來(lái)說(shuō)沒(méi)有太大意外。我們?nèi)珙A(yù)想的那樣看到了對(duì)Translate/DispatchMessage方法的調(diào)用,這證明SWT的消息循環(huán)和一般的本地程序是沒(méi)有本質(zhì)差別的。不過(guò)和SDK程序有所不同的是,這里使用了PeekMessage,而非傳統(tǒng)SDK程序中所使用的GetMessage。(事實(shí)上,現(xiàn)代的大多數(shù)UI框架也傾向于采用PeekMessage而非GetMessage,不信的話你可以自己去查查看。)

    ?

    為什么是 PeekMessage 而非 GetMessage 呢?這是因?yàn)椋撼瞬僮飨到y(tǒng)通過(guò)正常途徑發(fā)送來(lái)的消息以外,應(yīng)用程序通常還要額外使用一些內(nèi)部的消息,這些消息需要通過(guò)“非常規(guī)”的途徑進(jìn)行處理。如果使用 GetMessage 的話,它只有在應(yīng)用程序消息隊(duì)列中存在消息的時(shí)候才會(huì)被喚醒,那些“非常”消息就失去了獲得及時(shí)處理的機(jī)會(huì)。例如, SWT 就創(chuàng)建了一些用于線程通信的內(nèi)部消息,這些消息是 Display.syncExec Display.asyncExec 得以正常運(yùn)作的基礎(chǔ)。上面 filterMessage runDeferredEvents 方法就對(duì)此有所涉及。不過(guò)因?yàn)檫@些輔助方法和本文的主題沒(méi)有直接關(guān)系,所以我不打算對(duì)它們作什么說(shuō)明;如果你有興趣的話,可以自己去研究一下這些函數(shù)內(nèi)部究竟做了些什么。

    ?

    接下來(lái)我們看看 SWT 消息循環(huán)中另外一個(gè)意義不明的方法: sleep

    public ? boolean ?sleep?()?{

    ????checkDevice?();

    ????
    if ?(runMessages? && ?getMessageCount?()? != ? 0 )? return ? true ;

    ????
    if ?(OS.IsWinCE)?{

    ???????OS.MsgWaitForMultipleObjectsEx?(
    0 ,? 0 ,?OS.INFINITE,?OS.QS_ALLINPUT,?OS.MWMO_INPUTAVAILABLE);

    ???????
    return ? true ;

    ????}

    ????
    return ?OS.WaitMessage?();

    }

    中間的代碼明顯是針對(duì)WinCE系統(tǒng)的,可以不去管它。有點(diǎn)意外的是這里出現(xiàn)了WaitMessage,這是一般程序中比較少見(jiàn)的一個(gè)函數(shù)調(diào)用。不過(guò)認(rèn)真想想,原因大概也可以理解。PeekMessageGetMessage的不同之處在于:如果消息隊(duì)列中沒(méi)有消息可抓,那么GetMessage會(huì)釋放控制權(quán)讓其他程序運(yùn)行,而PeekMessage卻不會(huì)。即使是在搶占式多任務(wù)操作系統(tǒng)中,一個(gè)程序總是攥著控制權(quán)不放也不是好事。因此,如果真的沒(méi)有任何消息需要處理,那么WaitMessage將使線程處于睡眠狀態(tài),直到下個(gè)消息到來(lái)才再次喚醒——這也是SWT為什么把該方法定名為sleep的原因。

    ?

    通過(guò)上面的研究我們看到:拋開(kāi)無(wú)關(guān)的細(xì)節(jié),消息循環(huán)的處理本身是非常簡(jiǎn)單的。然而,這些研究尚不足以解決我們的疑惑。最關(guān)鍵的窗口過(guò)程究竟是在哪定義的呢?很顯然,我們需要追蹤窗口的創(chuàng)建過(guò)程,來(lái)找到定義窗口過(guò)程的地方。所以接下來(lái)的研究對(duì)象就是 Shell

    ?

    Shell 類(lèi)并沒(méi)有類(lèi)似 create 這樣的方法,因此我們可以合理的猜想:創(chuàng)建窗口的過(guò)程大概就放在構(gòu)造函數(shù)中。

    ?

    接下來(lái)我們跟蹤 Shell 的實(shí)現(xiàn)代碼來(lái)證實(shí)此猜想。不過(guò)有一點(diǎn)值得先作個(gè)說(shuō)明:你可能已經(jīng)知道, Shell 對(duì)象具有一個(gè)很深的繼承層次——它的直接父類(lèi)是 Decoration ,而這個(gè)類(lèi)的父類(lèi)又是 Canvas Canvas 的父類(lèi)是 Composite ,依此類(lèi)推。你必須知道這個(gè)層次的原因是: Shell 創(chuàng)建過(guò)程中經(jīng)常會(huì)用到祖先類(lèi)中的一些方法,同時(shí)也會(huì)重載祖先類(lèi)中的部分方法,因此在跟蹤代碼的時(shí)候,你也得根據(jù)方法的調(diào)用者實(shí)際所在的類(lèi),在這個(gè)類(lèi)層次中上下移動(dòng)。 Eclipse 提供的 Hierarchy 視圖是個(gè)不錯(cuò)的工具,可以讓它來(lái)幫助你,如下圖所示。小心不要迷路!


    ?

    經(jīng)過(guò)一番跟蹤,我們有了如下的發(fā)現(xiàn):

    l????????通常,我們調(diào)用的是型如Shell(Display)或者Shell(Display, style)這樣的構(gòu)造函數(shù)。這兩個(gè)構(gòu)造函數(shù)都會(huì)調(diào)用內(nèi)部的其他一些形式的構(gòu)造函數(shù),最終調(diào)用如下的形式:

    Shell(Display, Shell parent, int style, int handle);

    l???????? 上述方法的最后一步調(diào)用了createWidget()。這個(gè)方法的名字應(yīng)該讓你馬上有一種“我找到了”的感覺(jué);

    l???????? Shell本身并沒(méi)有定義createWidget()方法,實(shí)際上它調(diào)用的是Decorations.createWidget

    l???????? Decorations.createWidget其實(shí)并沒(méi)有做什么事,只是簡(jiǎn)單的調(diào)用上級(jí)(Canvas)的實(shí)現(xiàn),然后修改一些內(nèi)部狀態(tài)。不過(guò),Canvas并沒(méi)有重載createWidget,因此控制繼續(xù)向上,來(lái)到Scrollable

    l????????同樣,Scrollable.createWidget也是簡(jiǎn)單的向上調(diào)用。Control類(lèi)才是完成真正工作的地方。我們可以從代碼中看到,這個(gè)類(lèi)作了相當(dāng)多的工作:

    void?createWidget?()?{

    ????foreground?
    =?background?=?-1;

    ????checkOrientation?(parent);

    ????createHandle?();

    ????checkBackground?();

    ????checkBuffered?();

    ????register?();

    ????subclass?();

    ????setDefaultFont?();

    ????checkMirrored?();

    ????checkBorder?();

    ????
    if?((state?&?PARENT_BACKGROUND)?!=?0)?{

    ???????setBackground?();

    ????}

    }


    有經(jīng)驗(yàn)的讀者從名字應(yīng)當(dāng)能夠猜到,上面這么多方法中,createHandle才應(yīng)當(dāng)是真正值得我們關(guān)心的。

    void?createHandle?()?{

    ????
    int?hwndParent?=?widgetParent?();

    ????handle?
    =?OS.CreateWindowEx?(

    ???????widgetExtStyle?(),

    ???????windowClass?(),

    ???????
    null,

    ???????widgetStyle?(),

    ???????OS.CW_USEDEFAULT,?
    0,?OS.CW_USEDEFAULT,?0,

    ???????hwndParent,

    ???????
    0,

    ???????OS.GetModuleHandle?(
    null),

    ???????widgetCreateStruct?());

    ????….

    }


    我沒(méi)有把完整的代碼列出來(lái);因?yàn)椋热灰呀?jīng)看到了CreateWindowEx,就知道我們想找的東西已經(jīng)就在眼前,沒(méi)有必要再找下去了。

    ?

    createWindowEx方法必須指定要?jiǎng)?chuàng)建的窗口類(lèi)名字,也就是上面代碼中windowClass()方法所作的事情。我們接著看看這個(gè)類(lèi)名應(yīng)當(dāng)是什么。然而,我們發(fā)現(xiàn)windowClass()Control類(lèi)中定義為抽象方法:

    abstract TCHAR windowClass ();

    這意味著實(shí)際上類(lèi)的名字是由具體的子類(lèi)來(lái)指定的。所以我們還要繼續(xù)跟蹤下去。因?yàn)槔^承層次上每個(gè)類(lèi)都能夠改寫(xiě)這個(gè)方法,所以我們不應(yīng)該從現(xiàn)在的位置回頭向下,而是應(yīng)當(dāng)從最底層的Shell開(kāi)始向上找——這樣,你找到的第一個(gè)被重載的地方就是最終的實(shí)現(xiàn)。

    ?

    Shell的確實(shí)現(xiàn)了windowClass()方法,方法如下:

    TCHAR?windowClass?()?{

    ????
    if?(OS.IsSP)?return?DialogClass;

    ????
    if?((style?&?SWT.TOOL)?!=?0)?{

    ???????
    int?trim?=?SWT.TITLE?|?SWT.CLOSE?|?SWT.MIN?|?SWT.MAX?|?SWT.BORDER?|?SWT.RESIZE;

    ???????
    if?((style?&?trim)?==?0)?return?display.windowShadowClass;

    ????}

    ????
    return?parent?!=?null???DialogClass?:?super.windowClass?();

    }


    因?yàn)檫@里涉及到其他一些變量,所以其意圖最初看上去可能不是很明確。總體的邏輯大概是這樣的:如果Shell發(fā)現(xiàn)用戶要?jiǎng)?chuàng)建的是一個(gè)對(duì)話框,那么將返回Dialog的內(nèi)部類(lèi)名。否則,調(diào)用上級(jí)類(lèi)的實(shí)現(xiàn)(shadowClass則是SWT內(nèi)部維護(hù)的一個(gè)需要特殊處理的類(lèi))。

    ?

    因?yàn)?/span>Shell的實(shí)現(xiàn)調(diào)用了基類(lèi),所以我們還是要往上走。DecorationsCanvasComposite都沒(méi)有重載windowClass()方法。繼續(xù)來(lái)到Scrollable類(lèi)中,這個(gè)方法具有如下的實(shí)現(xiàn):

    TCHAR?windowClass?()?{

    ????
    return?display.windowClass;

    }


    現(xiàn)在線索轉(zhuǎn)到了Display類(lèi)。然而,windowClass只是Display類(lèi)的一個(gè)字段,而非方法,這個(gè)字段一定是在哪個(gè)地方得到了初始化。問(wèn)題就是:究竟在哪初始化的呢?

    ?

    好在,我們只需要在Display類(lèi)查找哪里修改了windowClass字段就可以了。很快可以發(fā)現(xiàn)如下的方法:

    protected?void?init?()?{

    ????
    super.init?();

    ???????

    ????
    /*?Create?the?callbacks?*/

    ????windowCallback?
    =?new?Callback?(this,?"windowProc",?4);?//$NON-NLS-1$

    ????windowProc?
    =?windowCallback.getAddress?();

    ????
    if?(windowProc?==?0)?error?(SWT.ERROR_NO_MORE_CALLBACKS);

    ????…

    ????
    /*?Use?the?character?encoding?for?the?default?locale?*/

    ????windowClass?
    =?new?TCHAR?(0,?WindowName?+?WindowClassCount,?true);

    ????windowShadowClass?
    =?new?TCHAR?(0,?WindowShadowName?+?WindowClassCount,?true);

    ????WindowClassCount
    ++;

    上面代碼中用到了兩個(gè)相關(guān)字段:windowName是一個(gè)實(shí)例變量,其值為“SWT_Window”;而windowClassCount則是一個(gè)靜態(tài)變量,沒(méi)有說(shuō)明初始值,那么就是默認(rèn)值0

    ?

    稍稍分析一下就能明白:當(dāng)init()方法第一次被調(diào)用的時(shí)候,windowClass將被設(shè)置為字符串“SWT_Window0”(你可以將TCHAR對(duì)象視為和字符串等同的東西),然后windowClassCount遞增。如果init()方法第二次被調(diào)用,那么下一個(gè)類(lèi)名將會(huì)是SWT_Window1。不過(guò),通常情況下我們的SWT程序僅有一個(gè)Display對(duì)象,也僅會(huì)初始化一次。也因此,所有頂層窗口的類(lèi)名都應(yīng)當(dāng)是“SWT_Window0”。

    ?

    你可以用SPY++或者Winsight32之類(lèi)的工具來(lái)證實(shí)這一點(diǎn)(如下圖)。

    知道了類(lèi)名以后怎么辦呢?還是要從消息機(jī)制的原理上找到線索。而在Windows中將一個(gè)窗口類(lèi)和窗口過(guò)程連接起來(lái)的關(guān)鍵是:調(diào)用RegisterClass或者RegisterClassEx,并將類(lèi)名和窗口過(guò)程的地址作為參數(shù)一并傳入。所以,下面我們的目標(biāo)是查找在哪里調(diào)用了RegisterClass

    ?

    因?yàn)?/span>windowClass是定義在Display類(lèi)中的,按照就近的原則,我們就從這里找起。果然不出所料,在init()方法接下來(lái)的部分就有這樣的代碼:

    /*?Register?the?SWT?window?class?*/

    ????
    int?hHeap?=?OS.GetProcessHeap?();

    ????
    int?hInstance?=?OS.GetModuleHandle?(null);

    ????WNDCLASS?lpWndClass?
    =?new?WNDCLASS?();

    ????lpWndClass.hInstance?
    =?hInstance;

    ????lpWndClass.lpfnWndProc?
    =?windowProc;

    ????lpWndClass.style?
    =?OS.CS_BYTEALIGNWINDOW?|?OS.CS_DBLCLKS;

    ????lpWndClass.hCursor?
    =?OS.LoadCursor?(0,?OS.IDC_ARROW);

    ????
    int?byteCount?=?windowClass.length?()?*?TCHAR.sizeof;

    ????lpWndClass.lpszClassName?
    =?OS.HeapAlloc?(hHeap,?OS.HEAP_ZERO_MEMORY,?byteCount);

    ????OS.MoveMemory?(lpWndClass.lpszClassName,?windowClass,?byteCount);

    ????OS.RegisterClass?(lpWndClass);


    init()方法的其他部分還注冊(cè)了另外一些輔助窗口,比如陰影窗口等;此外還注冊(cè)了一個(gè)全局鉤子。這些部分和消息機(jī)制的核心沒(méi)有直接關(guān)系,可以不去管它。關(guān)鍵在于這一行:

    ????lpWndClass.lpfnWndProc?=?windowProc;

    ?

    回頭看看,在init()方法的開(kāi)頭部分,windowProc成員是這樣初始化的:

    ????/*?Create?the?callbacks?*/

    ????windowCallback?
    =?new?Callback?(this,?"windowProc",?4);?//$NON-NLS-1$

    ????windowProc?
    =?windowCallback.getAddress?();

    ????
    if?(windowProc?==?0)?error?(SWT.ERROR_NO_MORE_CALLBACKS);


    這里出現(xiàn)了一個(gè)神秘的類(lèi):Callback。有Windows 編程經(jīng)驗(yàn)的讀者大概會(huì)回想起,在Windows消息機(jī)制中,Callback是一個(gè)非常核心的概念。雖然Java程序員或許不熟悉它,不過(guò)事實(shí)上它可謂是Windows中的“控制反轉(zhuǎn)”或曰“依賴注入”——早在Java和模式大行其道之前很久,Windows中的一些手法已經(jīng)暗合了最新的編程范式,只是當(dāng)時(shí)沒(méi)有人給它起一個(gè)聽(tīng)上去比較嚇人的名字而已。

    ?

    跑題了,回到正文上來(lái)。先不看Callback的實(shí)現(xiàn),從這段代碼我們大概可以猜到:

    l???????? Callback類(lèi)就是將OO的世界和非OO的世界連接起來(lái)的橋梁;

    l???????? Callback的構(gòu)造函數(shù)中,提供了處理消息的目標(biāo)對(duì)象和處理消息的方法名稱(chēng)。最后那個(gè)參數(shù)4你不妨先猜猜看是什么意思;

    l???????? CallbackgetAddress()返回的應(yīng)該是一個(gè)地址,也就是——你應(yīng)當(dāng)猜到了——正是回調(diào)函數(shù)的地址;

    l???????? Callback背后一定有某種魔法,把傳入的對(duì)象方法和getAddress返回的回調(diào)函數(shù)巧妙的連接起來(lái)。

    ?

    接下來(lái),我們要進(jìn)行的是這個(gè)歷程中最艱苦的部分:揭示Callback類(lèi)背后的神秘魔法。

    (未完待續(xù))?

    posted on 2006-06-03 12:46 TreeNode 閱讀(3480) 評(píng)論(10)  編輯  收藏 所屬分類(lèi): SWT,JFace和RCPJava技術(shù)

    Feedback

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-04 09:01 Jet Geng
    強(qiáng)人。期待下一篇。  回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-04 16:41 foxcai
    樓主.
    你后面的圖片都沒(méi)有貼上來(lái)啊

    能不能處理一下啊.
      回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-04 17:32 TreeNode
    答樓上,我已經(jīng)努力了一整天,不知是否因?yàn)槲恼绿L(zhǎng)格式復(fù)雜,這個(gè)HTML編輯器速度難以忍受而且頻頻出現(xiàn)腳本錯(cuò)誤,上傳文件也失敗。我覺(jué)得很失望。或許我會(huì)想其他辦法解決。  回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-05 21:57 foxcai
    樓主表著急.
    慢慢來(lái).
    期待你的下篇.  回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-05 22:43 foxcai
    看來(lái)樓主的Win編程也很厲害啊.
    看了樓主的文章感覺(jué)霍然開(kāi)朗,有了些感覺(jué).
    強(qiáng)烈期待下篇.

    樓主能不能寫(xiě)一些關(guān)于SWT線程的文章呢?  回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-05 23:51 TreeNode
    今天上傳文件仍然失敗,我放棄了。全文做成PDF格式,有興趣的可以到這里下載:

    http://www.yousendit.com/transfer.php?action=download&ufid=19BF243E3E7F9D9C


    或者如果有Eclipse中文社區(qū)帳號(hào)的話,這里也可以:

    http://www.eclipseworld.org/bbs/read.php?tid=5132


    SWT的線程,只要了解Display對(duì)象提供的幾個(gè)同步方法,其他方面和一般的Java線程沒(méi)有什么差別了。Eclipse.org上面的文章也說(shuō)得很明白,似乎沒(méi)有什么東西可寫(xiě)的。  回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-09 03:01 foxcai
    已經(jīng)下載了.

    希望樓主能有更多好的文章.  回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-19 11:15 hhh
    能否談?wù)剆wing呢!  回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2006-06-19 19:13 TreeNode
    Swing我不熟悉,不評(píng)論。
      回復(fù)  更多評(píng)論
      

    # re: SWT: 深入內(nèi)幕之消息機(jī)制探秘(上篇) 2007-06-29 12:39 SWT
    具體兩個(gè)線程間是如何通信的 ?能否給舉個(gè)實(shí)例!謝謝 !現(xiàn)在急用
    有的話!通知我一聲!非常感激!我的QQ是84750858  回復(fù)  更多評(píng)論
      

    主站蜘蛛池模板: 亚洲区小说区图片区QVOD| 三年片免费高清版| 日本永久免费a∨在线视频| 精品国产呦系列在线观看免费| 男女超爽视频免费播放| 国产日韩AV免费无码一区二区三区 | 久久久亚洲欧洲日产国码aⅴ| 亚洲精品乱码久久久久久下载| 亚洲AV无码国产在丝袜线观看| 亚洲人成777在线播放| 亚洲大尺码专区影院| 久久亚洲AV成人无码国产最大| 亚洲国产欧美日韩精品一区二区三区 | 久久免费的精品国产V∧| 黄网站色视频免费在线观看的a站最新 | 日本一区免费电影| 中文字幕亚洲图片| 亚洲人成亚洲人成在线观看| 亚洲国产亚洲综合在线尤物| 精品久久久久亚洲| 日本高清免费中文在线看| 久久久久国色av免费看| 成人毛片免费网站| 日韩特黄特色大片免费视频| 亚洲人成人77777网站| 亚洲人成免费电影| 国产VA免费精品高清在线| 四虎成年永久免费网站 | 日韩人妻无码免费视频一区二区三区 | 四虎影库久免费视频| 国产一区二区免费在线| 亚洲国产精品va在线播放| 亚洲精品色播一区二区| 成全视频免费观看在线看| 在线观看免费a∨网站| 亚洲AV无码久久精品成人| 国产亚洲情侣久久精品| 99视频免费播放| 亚洲国产av无码精品| 亚洲一区动漫卡通在线播放| 久久久久亚洲AV无码去区首|