日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DLL Hell

發(fā)布時(shí)間:2024/4/15 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DLL Hell 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

DLL Hell是因?yàn)?span style="color:#ff0000">DLL當(dāng)初是作為函數(shù)級(jí)共享庫(kù)設(shè)計(jì)的,并不能真正提供一個(gè)所必需的信息(導(dǎo)類的時(shí)候出現(xiàn)問題)



DLL動(dòng)態(tài)鏈接庫(kù)是程序復(fù)用的重要方式,DLL可以導(dǎo)出函數(shù),使函數(shù)被多個(gè)程序復(fù)用,DLL中的函數(shù)實(shí)現(xiàn)可以被修改而無需重新編譯和連接使用該DLL的應(yīng)用程序。作為一名面向?qū)ο蟮某绦騿T,希望DLL可以導(dǎo)出類,以便在類的層次上實(shí)現(xiàn)復(fù)用。所幸的是,DLL確實(shí)也可以導(dǎo)出類。

?

然而事實(shí)卻沒這么簡(jiǎn)單,導(dǎo)出類的DLL在維護(hù)和修改時(shí)有很多地方必需很小心,增加成員變量、修改導(dǎo)出類的基類等操作都可能導(dǎo)致意想不到的后果,也許用戶更新了最新版本的DLL庫(kù)后,應(yīng)用程序就再也不能工作了。這就是著名的DLL Hell(DLL地獄)問題。

?

DLL地獄問題是怎么產(chǎn)生的呢?看下面的例子,假設(shè)DLL有一個(gè)導(dǎo)出類ClassD1:

class ClassD

{

???????public:?????????int GetInt();

???????private:??????int m_i;

};

int ClassD::GetInt()

{

???????return m_i;

}

?

應(yīng)用程序使用現(xiàn)在的代碼來使用這個(gè)類:

ClassD d;

printf(“%d”, d.GetInt());

?

程序進(jìn)行正正常,沒有什么問題。后來DLL需要升級(jí),對(duì)ClassD進(jìn)行了修改,增加了一個(gè)成員變量,如下:

class ClassD //?修改后

{

???????public:?????????int GetInt();

???????private:??????int m_i2;??int m_i;

};

?

把新的DLL編譯連接完成后,復(fù)制到應(yīng)用程序目錄,這個(gè)倒楣的應(yīng)用程序調(diào)用GetInt方法恐怕再也無法得正確的值了。事實(shí)上它還算幸運(yùn)的,如果GetInt的實(shí)現(xiàn)改成如下這樣,那么它馬上就要出錯(cuò)退出了。

int ClassD::GetInt() //?修改后

{

???????return m_i++;

}

?

這樣的事情,稱它是個(gè)地獄(Hell)一點(diǎn)也不夸張。為什么會(huì)出錯(cuò)呢?我們要先從類實(shí)例的創(chuàng)建開始,看看使用一個(gè)類的工作過程。

?

首先,程序語句“ClassD d;”為這個(gè)類申請(qǐng)一塊內(nèi)存。這塊內(nèi)存保存該類的所有成員變量,以及虛函數(shù)表。內(nèi)存的大小由類的聲明決定,在應(yīng)用程序編譯時(shí)就已經(jīng)確定。

?

然后,當(dāng)調(diào)用“d.GetInt()”時(shí),把申請(qǐng)的這一塊內(nèi)存做為this指針傳給GetInt函數(shù),GetInt函數(shù)從this指向的位置開始,加上m_i應(yīng)有的偏移量,計(jì)算m_i所在的內(nèi)存位置,并從該位置取數(shù)據(jù)返回。m_i相對(duì)this的偏移量是由m_i在類中定義的位置決定的,定義在前的成員變量在內(nèi)存中也更靠前。這個(gè)偏移量在DLL編譯時(shí)確定。

?

當(dāng)ClassD的定義改為修改后的狀態(tài)時(shí),有些東西變了。

?

第一個(gè)變的是內(nèi)存的大小。因?yàn)樾薷暮蟮腃lassD多了一個(gè)成員變量,所以內(nèi)存也變大了。然而這一點(diǎn)應(yīng)用程序并不知道。

?

第二個(gè)變的是m_i的偏移地址。因?yàn)樵趍_i之前定義了一個(gè)m_i2,m_i的實(shí)現(xiàn)偏移地址實(shí)際已經(jīng)靠后了。所以d.GetInt()訪問的將是原來m_i后面的那個(gè)位置,而這個(gè)位置已經(jīng)超出原來那塊內(nèi)存的后部范圍了。

?

很顯然,在更換了DLL后,應(yīng)用程序還按原來的大小申請(qǐng)了一塊內(nèi)存,而它調(diào)用的方法卻訪問了比這塊內(nèi)存更大的區(qū)域,出錯(cuò)再在所難免。

?

同樣的情形還會(huì)發(fā)生在以下這些種情況中:

?

1)?應(yīng)用程序直接訪問類的公有變量,而該公有變量在新DLL中定義的位置發(fā)生了變化;

2)?應(yīng)用程序調(diào)用類的一個(gè)虛函數(shù),而新的類中,該虛函數(shù)的前面又增加了一個(gè)虛函數(shù);

3)?新類的后面增加了成員變量,并且新類的成員函數(shù)將訪問、修改這些變量;

4)?修改了新類的基類,基類的大小發(fā)生了變化;

?

等等,總言而之,一不小心,你的程序就會(huì)掉進(jìn)地獄。

通過對(duì)這些引起出錯(cuò)的情況進(jìn)行分析,會(huì)發(fā)現(xiàn)其實(shí)只有三點(diǎn)變化會(huì)引起出錯(cuò),因?yàn)檫@三點(diǎn)是使用這個(gè)DLL的應(yīng)用程序在編譯時(shí)就需要確定的內(nèi)容,它們分別是:

1)?類的大小;

2)?類成員的偏移地址;

3)?虛函數(shù)的順序。

?

要想做一個(gè)可升級(jí)的DLL,必需避免以上三個(gè)問題。所以以下三點(diǎn)用來使DLL遠(yuǎn)離地獄。

?

1,不直接生成類的實(shí)例。對(duì)于類的大小,當(dāng)我們定義一個(gè)類的實(shí)例,或使用new語句生成一個(gè)實(shí)例時(shí),內(nèi)存的大小是在編譯時(shí)決定的。要使應(yīng)用程序不依賴于類的大小,只有一個(gè)辦法:應(yīng)用程序不生成類的實(shí)例,使用DLL中的函數(shù)來生成。把導(dǎo)出類的構(gòu)造函數(shù)定義為私有的(privated),在導(dǎo)出類中提供靜態(tài)(static)成員函數(shù)(如NewInstance())用來生成類的實(shí)例。因?yàn)镹ewInstance()函數(shù)在新的DLL中會(huì)被重新編譯,所以總能返回大小正確的實(shí)例內(nèi)存。

?

2,不直接訪問成員變量。應(yīng)用程序直接訪問類的成員變量時(shí)會(huì)用到該變量的偏移地址。所以避免偏移地址依賴的辦法就是不要直接訪問成員變量。把所有的成員變量的訪問控制都定義為保護(hù)型(protected)以上的級(jí)別,并為需要訪問的成員變量定義Get或Set方法。Get或Set方法在編譯新DLL時(shí)會(huì)被重新編譯,所以總能訪問到正確的變量位置。

?

3,忘了虛函數(shù)吧,就算有也不要讓應(yīng)用程序直接訪問它。因?yàn)轭惖臉?gòu)造函數(shù)已經(jīng)是私有(privated)的了,所以應(yīng)用程序也不會(huì)去繼承這個(gè)類,也不會(huì)實(shí)現(xiàn)自己的多態(tài)。如果導(dǎo)出類的父類中有虛函數(shù),或設(shè)計(jì)需要(如類工場(chǎng)之類的框架),一定要把這些函數(shù)聲明為保護(hù)的(protected)以上的級(jí)別,并為應(yīng)用程序重新設(shè)計(jì)調(diào)用該慮函數(shù)的成員函數(shù)。這一點(diǎn)也類似于對(duì)成員變量的處理。

?

如果導(dǎo)出的類能遵循以上三點(diǎn),那么以后對(duì)DLL的升級(jí)將可以認(rèn)為是安全的。

?

如果對(duì)一個(gè)已經(jīng)存在的導(dǎo)出類的DLL進(jìn)行維護(hù),同樣也要注意:不要改動(dòng)所有的成員變量,包括導(dǎo)出類的父類,無論定義的順序還是數(shù)量;不要?jiǎng)铀械奶摵瘮?shù),無論順序還是數(shù)量。

?

總結(jié)起來,其實(shí)是一句話:只導(dǎo)出類的函數(shù) ??導(dǎo)出類的DLL不要導(dǎo)出除了函數(shù)以外的任何內(nèi)容。聽起來是不是有點(diǎn)可笑呢!

?

事實(shí)上,建議你在發(fā)布導(dǎo)出類的DLL的時(shí)候,重新定義一個(gè)類的聲明,這個(gè)聲明可以不管原來的類里的成員變量之類的,只把接口函數(shù)列在類的聲明里,如下面的例子:

class ClassInterface

{

???????privated:

??????????????ClassInterface();

???????public:

??????????????static ClassInterface * NewInstance();

??????????????int GetXXX();

??????????????void SetXXX();

??????????????void Function();

};

?

使用該DLL的應(yīng)用程序用上面的定義作為ClassInterface的頭文件,便不會(huì)有任何可能導(dǎo)致的安全問題。

?

DLL Hell問是歸根結(jié)底是因?yàn)?span style="color:#ff0000">DLL當(dāng)初是作為函數(shù)級(jí)共享庫(kù)設(shè)計(jì)的,并不能真正提供一個(gè)所必需的信息。類層上的程序復(fù)用只有Java和C#生成的類文件才能做到。

總結(jié)

以上是生活随笔為你收集整理的DLL Hell的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。