Effective BCB Form Program(1) ——窗體事件驅動鏈引發程序錯誤分析

BCB中採用的類庫是VCL,其編程框架是事件驅動的,類似於VB。我在開發過程中發現,如果不對BCB的事件驅動鏈進行分析,寫的程序會帶有很多的錯誤,健壯性很成問題,而且調試很麻煩。

我發現程序中的很多錯誤都來源於C++的指針操作。在程序中用new的方法創建了一個對象,然後delete這個對象之後,如果此時還有其它指針指向這個對象,訪問此對象信息的代碼必定會引發異常。這在C++中是常識性的問題。但這個問題在BCB這類事件驅動的開發環境中就複雜化了。由於事件模型其實是對Windows消息循環機制的一個封裝,而Windows中一個消息可能會引發一連串的其他消息,所以,事件之間也是相互引發的,形成一個事件驅動的鏈條。

BCB這類RAD的開發環境中,窗體(Form)是最核心的組件,窗體的事件模型也是程序中最需要分析的。

我在BCB5中設計了幾種典型情況,針對BCB提供的事件模型,繪出了BCB中的事件驅動鏈。由於WM_PAINT消息的反覆激發,所以我有意識地屏蔽掉了對這一消息的響應事件OnPaint。我發現大多數錯誤就發生成窗體的生成與銷燬過程中,其主要的原因就是程序員選錯了事件,將代碼放錯了地方。所以,我重點分析了工程中窗體的創建和銷燬過程中的事件驅動鏈。

 

先對BCB的工程文件(BPR)進行分析:

 

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

{

        try

        {

                 Application->Initialize();

                 Application->CreateForm(__classid(TForm1), &Form1);

                 Application->Run();

        }

        catch (Exception &exception)

        {

                 Application->ShowException(&exception);

        }

        return 0;

}

 

WinMainBCB工程的入口點,Initialize()一句完成程序的初始化工作,CreateForm()一句完成窗體的創建工作(在此引發一系列的事件),Run()一句則進入了消息循環,此時事件主要是用戶操作引發的(當然也有定時器等操作系統引發的事件)。

Catch()一句保證應用程序不會引起操作系統的崩潰。但這也是無數的BCBDelphi程序會引發“XXX內存讀寫錯誤”信息的根源所在。正是爲了提高BCB程序的健壯性,我才進行了這個實驗,其中比較枯燥,不想看記錄的人可以直接去看結論部分。

實驗記錄:

一、由BCB自動創建的Form的事件驅動鏈

實驗一工程中只有一個主窗體Form1

當窗體中只有一個主窗體(Form1)時,程序運行事件的發生次序如下:

創建

Form1構造函數àOnCreate()  //到此窗體創建完畢,以下進入窗體的顯示過程

àOnShow()àOnActivate()àOnCanResize()àOnConstrainedResize()àOnResize()

窗體顯示完畢,以下進入消息循環,等待用戶輸入.

關閉

關閉主窗體Form1:

OnCloseQuery()àOnClose()  //到此退出消息循環

àOnHide()àOnDestory()à~Form1()析構函數

 

顯然,一個窗體要先隱藏,再銷燬,最後再調用析構函數

另外:OnShow()à……àOnResize()這一事件鏈在各種情況中始終不變,下面的記錄中我就以……代表這些不變的事件鏈。

實驗二:工程中有兩個窗體,Form1爲主窗體,Form2爲普通窗體

當工程中有兩個窗體,Form1爲主窗體,Form2爲普通窗體,事件鏈如下:

創建

Form1構造函數à OnCreate() //到此窗體Form1創建完畢,開始創建Form2

à Form2構造函數

//以下進入Form1的顯示過程

àOnShow()à……àOnResize()

 

除非在代碼中指定,缺省情況下Form2只處於加載狀態,不會顯示,Form2中的OnCreate()等事件代碼不會運行。

關閉

關閉時,Form2處於隱藏或顯示狀態均一樣:

Form1OnCloseQuery()àOnClose()  //到此退出工程消息循環,以下銷燬Form2

àForm2析構函數àForm1 OnHide()àForm1OnDestory()à~Form1()析構函數

 

值得注意的是,這時,Form2OnDestory()沒有被執行,對應地,當它創建時,Form2OnCreate()也沒有執行!

實驗三:工程中有兩個窗體,Form1爲主窗體,Form2繼承自Form1

BCB中,可以很方便地繼承某個窗體,這是BCB的一個非常好用的功能。

創建

Form1構造函數à OnCreate() //到此窗體Form1創建完畢,開始創建Form2

àForm2OnCreate()àForm1的構造函數àForm2的構造函數  //以下進入Form1的顯示過程,同單窗體工程一樣àOnShow()à……àOnResize()

 

值得注意的是:Form2OnCreate()函數居然先於Form2的構造函數運行!

關閉

1Form2爲隱藏狀態,

Form1OnCloseQuery()àOnClose()  //到此退出工程消息循環,以下銷燬Form2

àForm2析構函數àForm1的析構函數àForm2OnDestory()àForm1 OnHide()àForm1OnDestory()àForm1的析構函數

 

注意:Form1的析構函數被執行了兩次!

 

2Form2爲顯示狀態

Form1OnCloseQuery()àOnClose()  //到此退出工程消息循環,以下銷燬Form2

àForm2OnHide()àForm2的析構函數à Form1的析構函數à Form2OnDestory()àForm1 OnHide()àForm1OnDestory()àForm1的析構函數

 

注意:Form1的析構函數被執行了兩次!

兩種情況對比,可以發現只多了一個Form2的隱藏過程.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章