RTY 實(shí)踐出真知
posts - 319, comments - 22, trackbacks - 0, articles - 11
C++博客
::
首頁(yè)
::
新隨筆
::
聯(lián)系
::
聚合
::
管理
DLL導(dǎo)出類(lèi)的問(wèn)題
Posted on 2011-08-10 07:23
RTY
閱讀(837)
評(píng)論(0)
編輯
收藏
引用
所屬分類(lèi):
C/C++
、
Windows
DLL導(dǎo)出類(lèi)的問(wèn)題
http://www.diybl.com/course/3_program/c++/cppjs/200833/102641.html
DLL動(dòng)態(tài)鏈接庫(kù)是程序復(fù)用的重要方式,DLL可以導(dǎo)出函數(shù),使函數(shù)被多個(gè)程序復(fù)用,DLL中的函數(shù)實(shí)現(xiàn)可以被修改而無(wú)需重新編譯和連接使用該DLL的應(yīng)用程序。作為一名面向?qū)ο蟮某绦騿T,希望DLL可以導(dǎo)出類(lèi),以便在類(lèi)的層次上實(shí)現(xiàn)復(fù)用。所幸的是,DLL確實(shí)也可以導(dǎo)出類(lèi)。
然而事實(shí)卻沒(méi)這么簡(jiǎn)單,導(dǎo)出類(lèi)的DLL在維護(hù)和修改時(shí)有很多地方必需很小心,增加成員變量、修改導(dǎo)出類(lèi)的基類(lèi)等操作都可能導(dǎo)致意想不到的后果,也許
用戶
更新了最新版本的DLL庫(kù)后,應(yīng)用程序就再也不能
工作
了。這就是著名的DLL Hell(DLL地獄)問(wèn)題。
DLL地獄問(wèn)題是怎么產(chǎn)生的呢?看下面的例子,假設(shè)DLL有一個(gè)導(dǎo)出類(lèi)ClassD1:
class ClassD
{
public:
int GetInt();
private:
int m_i;
};
int ClassD::GetInt()
{
return m_i;
}
應(yīng)用程序使用現(xiàn)在的代碼來(lái)使用這個(gè)類(lèi):
ClassD d;
printf(“%d”, d.GetInt());
程序進(jìn)行正正常,沒(méi)有什么問(wèn)題。后來(lái)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方法恐怕再也無(wú)法得正確的值了。事實(shí)上它還算幸運(yùn)的,如果GetInt的實(shí)現(xiàn)改成如下這樣,那么它馬上就要出錯(cuò)退出了。
int ClassD::GetInt() // 修改后
{
return m_i++;
}
這樣的事情,稱(chēng)它是個(gè)地獄(Hell)一點(diǎn)也不夸張。為什么會(huì)出錯(cuò)呢?我們要先從類(lèi)實(shí)例的創(chuàng)建開(kāi)始,看看使用一個(gè)類(lèi)的工作過(guò)程。
首先,程序語(yǔ)句“ClassD d;”為這個(gè)類(lèi)申請(qǐng)一塊內(nèi)存。這塊內(nèi)存保存該類(lèi)的所有成員變量,以及虛函數(shù)表。內(nèi)存的大小由類(lèi)的聲明決定,在應(yīng)用程序編譯時(shí)就已經(jīng)確定。
然后,當(dāng)調(diào)用“d.GetInt()”時(shí),把申請(qǐng)的這一塊內(nèi)存做為this指針傳給GetInt函數(shù),GetInt函數(shù)從this指向的位置開(kāi)始,加上m_i應(yīng)有的偏移量,計(jì)算m_i所在的內(nèi)存位置,并從該位置取數(shù)據(jù)返回。m_i相對(duì)this的偏移量是由m_i在類(lè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()訪問(wèn)的將是原來(lái)m_i后面的那個(gè)位置,而這個(gè)位置已經(jīng)超出原來(lái)那塊內(nèi)存的后部范圍了。
很顯然,在更換了DLL后,應(yīng)用程序還按原來(lái)的大小申請(qǐng)了一塊內(nèi)存,而它調(diào)用的方法卻訪問(wèn)了比這塊內(nèi)存更大的區(qū)域,出錯(cuò)再在所難免。
同樣的情形還會(huì)發(fā)生在以下這些種情況中:
1)
應(yīng)用程序直接訪問(wèn)類(lèi)的公有變量,而該公有變量在新DLL中定義的位置發(fā)生了變化;
2)
應(yīng)用程序調(diào)用類(lèi)的一個(gè)虛函數(shù),而新的類(lèi)中,該虛函數(shù)的前面又增加了一個(gè)虛函數(shù);
3)
新類(lèi)的后面增加了成員變量,并且新類(lèi)的成員函數(shù)將訪問(wèn)、修改這些變量;
4)
修改了新類(lèi)的基類(lèi),基類(lèi)的大小發(fā)生了變化;
等等,總言而之,一不小心,你的程序就會(huì)掉進(jìn)地獄。通過(guò)對(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)
類(lèi)的大小;
2)
類(lèi)成員的偏移地址;
3)
虛函數(shù)的順序。
要想做一個(gè)可升級(jí)的DLL,必需避免以上三個(gè)問(wèn)題。所以以下三點(diǎn)用來(lái)使DLL遠(yuǎn)離地獄。
1,不直接生成類(lèi)的實(shí)例。對(duì)于類(lèi)的大小,當(dāng)我們定義一個(gè)類(lèi)的實(shí)例,或使用new語(yǔ)句生成一個(gè)實(shí)例時(shí),內(nèi)存的大小是在編譯時(shí)決定的。要使應(yīng)用程序不依賴于類(lèi)的大小,只有一個(gè)辦法:應(yīng)用程序不生成類(lèi)的實(shí)例,使用DLL中的函數(shù)來(lái)生成。把導(dǎo)出類(lèi)的構(gòu)造函數(shù)定義為私有的(privated),在導(dǎo)出類(lèi)中提供靜態(tài)(static)成員函數(shù)(如NewInstance())用來(lái)生成類(lèi)的實(shí)例。因?yàn)镹ewInstance()函數(shù)在新的DLL中會(huì)被重新編譯,所以總能返回大小正確的實(shí)例內(nèi)存。
2,不直接訪問(wèn)成員變量。應(yīng)用程序直接訪問(wèn)類(lèi)的成員變量時(shí)會(huì)用到該變量的偏移地址。所以避免偏移地址依賴的辦法就是不要直接訪問(wèn)成員變量。把所有的成員變量的訪問(wèn)控制都定義為保護(hù)型(protected)以上的級(jí)別,并為需要訪問(wèn)的成員變量定義Get或Set方法。Get或Set方法在編譯新DLL時(shí)會(huì)被重新編譯,所以總能訪問(wèn)到正確的變量位置。
3,忘了虛函數(shù)吧,就算有也不要讓?xiě)?yīng)用程序直接訪問(wèn)它。因?yàn)轭?lèi)的構(gòu)造函數(shù)已經(jīng)是私有(privated)的了,所以應(yīng)用程序也不會(huì)去繼承這個(gè)類(lèi),也不會(huì)實(shí)現(xiàn)自己的多態(tài)。如果導(dǎo)出類(lèi)的父類(lèi)中有虛函數(shù),或設(shè)計(jì)需要(如類(lèi)工場(chǎng)之類(lèi)的框架),一定要把這些函數(shù)聲明為保護(hù)的(protected)以上的級(jí)別,并為應(yīng)用程序重新設(shè)計(jì)調(diào)用該慮函數(shù)的成員函數(shù)。這一點(diǎn)也類(lèi)似于對(duì)成員變量的處理。
如果導(dǎo)出的類(lèi)能遵循以上三點(diǎn),那么以后對(duì)DLL的升級(jí)將可以認(rèn)為是安全的。
如果對(duì)一個(gè)已經(jīng)存在的導(dǎo)出類(lèi)的DLL進(jìn)行維護(hù),同樣也要注意:不要改動(dòng)所有的成員變量,包括導(dǎo)出類(lèi)的父類(lèi),無(wú)論定義的順序還是數(shù)量;不要?jiǎng)铀械奶摵瘮?shù),無(wú)論順序還是數(shù)量。
總結(jié)起來(lái),其實(shí)是一句話:導(dǎo)出類(lèi)的DLL不要導(dǎo)出除了函數(shù)以外的任何內(nèi)容。聽(tīng)起來(lái)是不是有點(diǎn)可笑呢!
事實(shí)上,建議你在發(fā)布導(dǎo)出類(lèi)的DLL的時(shí)候,重新定義一個(gè)類(lèi)的聲明,這個(gè)聲明可以不管原來(lái)的類(lèi)里的成員變量之類(lèi)的,只把接口函數(shù)列在類(lèi)的聲明里,如下面的例子:
class ClassInterface
{
privated:
ClassInterface();
public:
static ClassInterface * NewInstance();
int GetXXX();
void SetXXX();
void Function();
};
使用該DLL的應(yīng)用程序用上面的定義作為ClassInterface的頭文件,便不會(huì)有任何可能導(dǎo)致的安全問(wèn)題。
DLL地獄問(wèn)是歸根結(jié)底是因?yàn)镈LL當(dāng)初是作為函數(shù)級(jí)共享庫(kù)設(shè)計(jì)的,并不能真正提供一個(gè)類(lèi)所必需的信息。類(lèi)層上的程序復(fù)用只有Java和C#生成的類(lèi)文件才能做到。
只有注冊(cè)用戶
登錄
后才能發(fā)表評(píng)論。
【推薦】100%開(kāi)源!大型工業(yè)跨平臺(tái)軟件C++源碼提供,建模,組態(tài)!
相關(guān)文章:
Dynamic Library Design Guidelines (Xcode)
Qt internal error: qt_menu.nib could not be loaded.
Cocoa讀取和寫(xiě)入plist文件
什么叫IOC(編程術(shù)語(yǔ))
otool 與dylib
找不到 dirent.h 文件
vc調(diào)試窗口表達(dá)式格式化資料
Context Operator (C/C++ Language Expressions)
Managed Expressions in C++ (VC 2010 調(diào)試)
Find path of an application
網(wǎng)站導(dǎo)航:
博客園
IT新聞
BlogJava
博問(wèn)
Chat2DB
管理
Powered by:
C++博客
Copyright © RTY
日歷
<
2011年9月
>
日
一
二
三
四
五
六
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
常用鏈接
我的隨筆
我的評(píng)論
我參與的隨筆
留言簿
(12)
給我留言
查看公開(kāi)留言
查看私人留言
隨筆分類(lèi)
(538)
C/C++(70)
CSS(41)
JavaScript(6)
Linux(7)
Lua專(zhuān)題(3)
Mac os(34)
Python(33)
QML(21)
Qt(65)
TBB(4)
Windows(38)
XML
編程常識(shí)(37)
軟件(38)
數(shù)據(jù)庫(kù)
需要注意的(14)
質(zhì)量保障(14)
轉(zhuǎn)載隨筆(113)
隨筆檔案
(319)
2013年3月 (2)
2013年2月 (5)
2013年1月 (4)
2012年12月 (2)
2012年11月 (7)
2012年10月 (1)
2012年9月 (1)
2012年7月 (1)
2012年6月 (2)
2012年5月 (5)
2012年4月 (15)
2012年3月 (13)
2012年2月 (5)
2012年1月 (1)
2011年12月 (1)
2011年11月 (5)
2011年10月 (5)
2011年9月 (26)
2011年8月 (51)
2011年7月 (23)
2011年6月 (22)
2011年5月 (82)
2011年4月 (38)
2010年12月 (2)
文章分類(lèi)
(11)
C/C++文章收集
Linux(3)
QT相關(guān)資料收集(8)
文章檔案
(11)
2010年12月 (11)
官方教程
知識(shí)共享鏈接
搜索
最新評(píng)論
1.?re: OSGi開(kāi)發(fā)起步
評(píng)論內(nèi)容較長(zhǎng),點(diǎn)擊標(biāo)題查看
--M.I
2.?re: iOS中plist的創(chuàng)建,數(shù)據(jù)寫(xiě)入與讀取
評(píng)論內(nèi)容較長(zhǎng),點(diǎn)擊標(biāo)題查看
--泰國(guó)
3.?re: Windows 7使用技巧:在當(dāng)前路徑下打開(kāi)命令行(cmd)命令窗口
給力啊
--gy
4.?re: codesign CSSMERR_TP_NOT_TRUSTED
你好,能告訴我你QQ么,我遇到了這問(wèn)題,但是安裝了證書(shū)后還是這樣,能幫我看看么
--潮
5.?re: Windows 7使用技巧:在當(dāng)前路徑下打開(kāi)命令行(cmd)命令窗口
好用
--xxoo
閱讀排行榜
1.?找不到 dirent.h 文件(9708)
2.?實(shí)現(xiàn)Qt日志功能并輸出到文件(qDebug\qWarning\ qCritical\qFatal)(9321)
3.?VirtualBox虛擬機(jī)安裝Mac OS X Lion(8953)
4.?Windows 7使用技巧:在當(dāng)前路徑下打開(kāi)命令行(cmd)命令窗口(8142)
5.?iOS中plist的創(chuàng)建,數(shù)據(jù)寫(xiě)入與讀取(7735)
評(píng)論排行榜
1.?字符集編碼與 C/C++ 源文件字符編譯亂彈(收集轉(zhuǎn)載)(4)
2.?Windows 7使用技巧:在當(dāng)前路徑下打開(kāi)命令行(cmd)命令窗口(4)
3.?Intel Parallel Studio XE 2011(3)
4.?VirtualBox虛擬機(jī)安裝Mac OS X Lion(2)
5.?QML與c++交互學(xué)習(xí)筆記(八) qt c++直接調(diào)用QML中的函數(shù), 直接設(shè)置屬性 (2)
天堂无码久久综合东京热
|
浪潮AV色综合久久天堂
|
99久久精品国产综合一区
|
99久久精品免费看国产
|
亚洲精品国产第一综合99久久
|
伊人久久大香线蕉综合网站
|
久久AV高清无码
|
精品久久久久久久久久中文字幕
|
色欲久久久天天天综合网
|
久久久亚洲欧洲日产国码二区
|
亚洲国产精品久久久久婷婷老年
|
久久久WWW成人免费毛片
|
人妻丰满AV无码久久不卡
|
国产精品综合久久第一页
|
精品少妇人妻av无码久久
|
久久这里的只有是精品23
|
久久精品免费观看
|
亚洲狠狠婷婷综合久久蜜芽
|
久久强奷乱码老熟女
|
国产99久久久久久免费看
|
嫩草伊人久久精品少妇AV
|
久久有码中文字幕
|
久久免费视频6
|
精品视频久久久久
|
国产一区二区精品久久岳
|
97久久精品无码一区二区
|
色综合久久久久无码专区
|
国产成人精品综合久久久久
|
久久免费大片
|
亚洲日本va午夜中文字幕久久
|
国产成人无码精品久久久久免费
|
久久99精品国产麻豆宅宅
|
WWW婷婷AV久久久影片
|
国产高潮国产高潮久久久
|
亚洲欧洲日产国码无码久久99
|
久久免费看黄a级毛片
|
少妇无套内谢久久久久
|
中文国产成人精品久久不卡
|
亚洲国产精品成人久久
|
国产精品欧美久久久天天影视
|
久久香蕉一级毛片
|