【概述】
相信經常使用接口的朋友們,經常碰到訪問違規異常(Access violation),很多情況下無法理解,認為是編譯器的Bug,然后去繞開它,不追其根源,把責任推給IDE,推給編譯器(其實本人以前也經常這樣想)。其實每個異常都是有原因的,碰到這種問題不要繞開,如果目前無法解決,至少要清楚的知道它出現的起因,不放過每一次追根到底的機會。這才是做程序員的應有的心態。(好像有點扯遠了…)
【問題描述】
今天公司外包模塊中,外包人員反應出現一個很奇怪的問題,說模塊中無故出現了AV異常。調試果然如此,而且每次都能出現,每次都能出現的異常就好解決,
最初的代碼如下:
一個點擊按鈕事件中包含如下一段代碼:
begin .... (TmBeanFrameVars.GetBean('ef_pcy_frmTree') as INormalSelector).executeSelect(lvPass); .... end;
這樣程序在退出函數的時候引發了一個AV異常(end;運行之后),這時候一般對接口不了解的都無從下手。也無從下手進行調試。
【問題分析】
首先上面這句簡單的訪問其實會出現接口的臨時變量,而這個變量在函數退出的時候會執行清理,因為executeSelect函數中釋放了插件的實例,然后清理臨時接口變量時會觸發接口對象的__release方法。這個時候引發的一個異常。
我改造一下代碼,讓錯誤在函數退出之前出現,完整代碼如下。
我用兩個局部變量,這樣改造后,在執行lvIntf := nil的時候就會出現AV錯誤。
我順便把executeSelect代碼貼一下,可以看到這個插件是一個窗體對象實例。在執行這個函數里面會把窗體顯示,然后進行了釋放,然后退出了函數。
大概的原因明白后,我們調整下代碼,代碼如下:
注意紅色部分,選取完后(釋放實例后),然后馬上清理接口變量,這個時候,其實內存塊應該還是完整的(個人推測),所以清理時不會出現訪問違規異常。
下面我來做個調試證明我的推測
看紅色框出來部分,兩個接口變量在釋放完后,和之前的指向的內存塊中值是一樣的(我只獲取了一個值,其實可以進行完整對比),然繼續執行。
在看看紅色部分,這個時候(其實執行玩cdsOrgan.Append就出現了),lvIntf指向的內存塊已經被清理了,因為有新的內存申請。所以后面在清理lvIntf := nil的時候出現訪問違規錯誤。
好了到了這個時候,我們應該發現引發異常的真正原因了:在清理接口變量時,訪問了一塊不可預知的內存塊,所以導致了訪問違規錯誤。所以請大家在使用接口過程中注意接口的清理工作。
*認真閱讀會收獲更多。
==========================================
DIOCP官方社區|MyBean官方社區
==========================================