【概述】
相信經常使用接口的朋友們,經常碰到通路違規異常(Access violation),很多情況下無法了解,認為是編譯器的Bug,然後去繞開它,不追其根源,把責任推給IDE,推給編譯器(其實本人以前也經常這樣想)。其實每個異常都是有原因的,碰到這種問題不要繞開,如果目前無法解決,至少要清楚的知道它出現的起因,不放過每一次追根到底的機會。這才是做程式員的應有的心态。(好像有點扯遠了…)
【問題描述】
今天公司外包子產品中,外包人員反應出現一個很奇怪的問題,說子產品中無故出現了AV異常。調試果然如此,而且每次都能出現,每次都能出現的異常就好解決,
最初的代碼如下:
一個點選按鈕事件中包含如下一段代碼:
這樣程式在退出函數的時候引發了一個AV異常(end;運作之後),這時候一般對接口不了解的都無從下手。也無從下手進行調試。
【問題分析】
首先上面這句簡單的通路其實會出現接口的臨時變量,而這個變量在函數退出的時候會執行清理,因為executeSelect函數中釋放了插件的執行個體,然後清理臨時接口變量時會觸發接口對象的__release方法。這個時候引發的一個異常。
我改造一下代碼,讓錯誤在函數退出之前出現,完整代碼如下。

我用兩個局部變量,這樣改造後,在執行lvIntf := nil的時候就會出現AV錯誤。
我順便把executeSelect代碼貼一下,可以看到這個插件是一個窗體對象執行個體。在執行這個函數裡面會把窗體顯示,然後進行了釋放,然後退出了函數。
大概的原因明白後,我們調整下代碼,代碼如下:
注意紅色部分,選取完後(釋放執行個體後),然後馬上清理接口變量,這個時候,其實記憶體塊應該還是完整的(個人推測),是以清理時不會出現通路違規異常。
下面我來做個調試證明我的推測
看紅色框出來部分,兩個接口變量在釋放完後,和之前的指向的記憶體塊中值是一樣的(我隻擷取了一個值,其實可以進行完整對比),然繼續執行。
在看看紅色部分,這個時候(其實執行玩cdsOrgan.Append就出現了),lvIntf指向的記憶體塊已經被清理了,因為有新的記憶體申請。是以後面在清理lvIntf := nil的時候出現通路違規錯誤。
好了到了這個時候,我們應該發現引發異常的真正原因了:在清理接口變量時,通路了一塊不可預知的記憶體塊,是以導緻了通路違規錯誤。是以請大家在使用接口過程中注意接口的清理工作。
*認真閱讀會收獲更多。
==========================================
<a href="http://www.diocp.org/">http://www.diocp.org/</a>