• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            白駒過隙
            學(xué)而不思則罔,思而不學(xué)則怠
            posts - 4,  comments - 3,  trackbacks - 0

            ?? 還有一種方法可以顯式導(dǎo)出類成員函數(shù),就是采用虛函數(shù)表的方法。 COM 就是這樣做的。當(dāng)在類中聲明一組虛函數(shù)的時(shí)候, 編譯器會(huì)創(chuàng)建一個(gè)虛函數(shù)表,將各虛函數(shù)的地址按聲明的順序放入其中。當(dāng)一個(gè)類對(duì)象被創(chuàng)建時(shí),它的前四個(gè)字節(jié)是一個(gè)指針,指向這個(gè)虛函數(shù)表。所以,修改以上的代碼:

            //CTry.h

            ??? virtual void print() ; // 將這個(gè)成員函數(shù)聲明為虛函數(shù)

            TRY_API CTry* createObject() ; // 聲明一個(gè)全局函數(shù),創(chuàng)建一個(gè) Ctry 對(duì)象的實(shí)例

            //CTry.cpp

            CTry *createObject()

            {

            ??? return new CTry() ;

            }

            ?

            在測(cè)試工程中,顯式導(dǎo)出該全局函數(shù),并通過它調(diào)用類成員函數(shù)。

            typedef CTry* (*Fn_FunType)() ;

            Fn_FunType pFun=(Fn_FunType)::GetProcAddress(hInstance ,"?createObject@@YAPAVCTry@@XZ") ;

            CTry * pTry = pFun() ;

            pTry ->print() ;

            delete pTry ;


            ?? 這個(gè)方法雖然同樣能達(dá)到顯式導(dǎo)出類成員函數(shù)的目的。但是上面的方法有個(gè)缺點(diǎn),就是在DLLnew 出來的對(duì)象,卻要放到客戶端釋放。因?yàn)榭蛻舳瞬⒉恢?/span>DLL中的實(shí)現(xiàn)方法,很可能會(huì)不知道釋放這個(gè)對(duì)象,從而造成內(nèi)存泄露。<<Effiective C++>>中明文規(guī)定禁止這樣做。

            ?

            ? 現(xiàn)在要找一個(gè)方法可以把回收內(nèi)存的任務(wù)交 DLL 自己來處理。有沒有這樣一個(gè)方法呢?當(dāng)然有,它就是引用計(jì)數(shù)。引用計(jì)數(shù)是一個(gè)簡(jiǎn)單的垃圾回收機(jī)制,它的原理很簡(jiǎn)單,組件內(nèi)部維護(hù)著一個(gè)引用計(jì)數(shù)的數(shù)值,當(dāng)用戶從組件取得一個(gè)接口時(shí),該數(shù)值 +1 ,當(dāng)用戶使用完這個(gè)接口,并釋放該接口時(shí),該數(shù)值 -1 ,當(dāng)該數(shù)值為 0 時(shí),組件將自己從內(nèi)存中刪除。下面是采用了引用計(jì)數(shù)的 DLL 實(shí)現(xiàn)方法。


            #ifdef?TRY_EXPORTS
            #define ?TRY_API?__declspec(dllexport)
            #else
            #define ?TRY_API?__declspec(dllimport)
            #endif
            #include?
            < iostream >
            using ? namespace ?std;

            // ?此類是從?Try.dll?導(dǎo)出的
            class ??CTry? {
            public :
            ????friend?TRY_API?CTry
            * ?createObject()?;

            ????
            virtual ?? void ?print()?;

            ????
            virtual ? void ?addRef()?;

            ????
            virtual ? void ?removeRef()?;
            protected :
            ????
            int ?refCount?;

            ????
            // 不允許通過構(gòu)造函數(shù)初始化對(duì)象
            ????CTry( void )?;
            ????
            ????
            virtual ? ~ CTry()?;


            ????
            // 不允許相互賦值,這里面細(xì)節(jié)太多,懶得去分析:D
            ????CTry(CTry? & ths)?;

            ????CTry
            * ? operator = ?( const ?CTry & ?ths?)?;
            ????
            }
            ;

            extern ?TRY_API? int ?nTry;

            TRY_API?
            int ?fnTry( void );
            ?
            TRY_API?CTry
            * ?createObject()?;

            // ?Try.cpp?:?定義?DLL?應(yīng)用程序的入口點(diǎn)。
            //

            #include?
            " stdafx.h "
            #include?
            " Try.h "


            #ifdef?_MANAGED
            #pragma?managed(push,?off)
            #endif

            BOOL?APIENTRY?DllMain(?HMODULE?hModule,
            ???????????????????????DWORD??ul_reason_for_call,
            ???????????????????????LPVOID?lpReserved
            ?????????????????????)
            {
            ????
            switch ?(ul_reason_for_call)
            ????
            {
            ????
            case ?DLL_PROCESS_ATTACH:
            ????
            case ?DLL_THREAD_ATTACH:
            ????
            case ?DLL_THREAD_DETACH:
            ????
            case ?DLL_PROCESS_DETACH:
            ????????
            break ;
            ????}

            ????
            return ?TRUE;
            }


            #ifdef?_MANAGED
            #pragma?managed(pop)
            #endif

            // ?這是導(dǎo)出變量的一個(gè)示例
            TRY_API? int ?nTry? = ? 0 ;

            // ?這是導(dǎo)出函數(shù)的一個(gè)示例。
            TRY_API? int ?fnTry( void )
            {
            ????
            return ? 42 ;
            }


            // ?這是已導(dǎo)出類的構(gòu)造函數(shù)。
            // ?有關(guān)類定義的信息,請(qǐng)參閱?Try.h

            void ?CTry::print()
            {
            ????cout
            << " CTry " << endl;
            }


            ?CTry?
            * createObject()
            ?
            {
            ?????CTry?
            * pTry? = ? new ?CTry()?;
            ?????pTry
            -> addRef()?;
            ?????
            return ?pTry?;
            ?}


            ?
            void ?CTry::removeRef()
            ?
            {
            ?????
            if ?( -- refCount? == ? 0 )
            ?????
            {
            ?????????delete?
            this ?; // 刪除自己
            ?????}

            ?}


            ?
            void ?CTry::addRef()
            ?
            {
            ?????
            ++ refCount?;
            ?}


            ?CTry::CTry()
            ?
            {
            ?????refCount?
            = ? 0 ?;
            ?}


            ?CTry::
            ~ CTry()
            ?
            {

            ?}

            ?CTry::CTry(CTry?
            & ths)
            ?
            {

            ?}


            ?CTry?
            * CTry:: operator ? = ( const ?CTry? & ths)
            ?
            {
            ?????
            return ?NULL;
            ?}

            ?

            ? 上面的方法中,用戶不能通過構(gòu)造函數(shù)來實(shí)例化一個(gè)對(duì)象,必須通過 createObject 接口才能實(shí)例化對(duì)象,當(dāng)用戶使用完這個(gè)對(duì)象時(shí)必須調(diào)用 removeRef() 接口。代碼如下:

            #include? " stdafx.h "
            #include?
            < iostream >
            #include?
            " Try.h "
            using ? namespace ?std;

            typedef?CTry
            * ?( * Fn_FunType)()?;

            int ?main()
            {

            ????HINSTANCE?hInstance?
            = ? 0 ;
            ????hInstance?
            = ?LoadLibrary( " Try.dll " )?;
            ????
            if ?(hInstance)
            ????
            {
            ????????Fn_FunType?pFun?
            = ?(Fn_FunType)::GetProcAddress(hInstance?, " ?createObject@@YAPAVCTry@@XZ " )?;

            ????????
            if ?(pFun)
            ????????
            {
            ????????????
            // (pTry->*pFun)()?;
            ????????????CTry * ?pTry? = ?pFun()?;
            ????????????pTry
            -> print()?;
            ????????????pTry
            -> removeRef();
            ????????}

            ????}

            ????system(
            " pause " )?;
            ????
            return ? 0 ?;
            }



            ? 當(dāng)然上面只是我為了方便測(cè)試而寫的一個(gè)簡(jiǎn)單的例子,還不完善。引用計(jì)數(shù)還有很多細(xì)節(jié)和注意事項(xiàng)。具體請(qǐng)看《more effective c++》第29項(xiàng)。

            ?

            關(guān)于名字修飾約定

            ?

            ?? 前面代碼我都是通過編譯器中的修飾名來調(diào)用相應(yīng)的函數(shù)的,修飾名是編譯器在編譯函數(shù)定義或者原型時(shí)生成的字符串。比如 "?createObject@@YAPAVCTry@@XZ" 就是 CTry::createObject 的修飾名。

            名字修飾約定隨調(diào)用約定和編譯種類 (C C++) 的不同而變化。函數(shù)名修飾約定隨編譯種類和調(diào)用約定的不同而不同,下面分別說明。

            A C 編譯時(shí)函數(shù)名修飾約定規(guī)則:

            __stdcall 調(diào)用約定在輸出函數(shù)名前加上一個(gè)下劃線前綴,后面加上一個(gè) "@" 符號(hào)和其參數(shù)的字節(jié)數(shù),格式為 _functionname@number

            __cdecl 調(diào)用約定僅在輸出函數(shù)名前加上一個(gè)下劃線前綴,格式為 _functionname

            __fastcall 調(diào)用約定在輸出函數(shù)名前加上一個(gè) "@" 符號(hào),后面也是一個(gè) "@" 符號(hào)和其參數(shù)的字節(jié)數(shù),格式為 @functionname@number

            它們均不改變輸出函數(shù)名中的字符大小寫,這和 PASCAL 調(diào)用約定不同, PASCAL 約定輸出的函數(shù)名無任何修飾且全部大寫。

            B C++ 編譯時(shí)函數(shù)名修飾約定規(guī)則:

            __stdcall 調(diào)用約定:

            1 、以 "?" 標(biāo)識(shí)函數(shù)名的開始,后跟函數(shù)名;

            2 、函數(shù)名后面以 "@@YG" 標(biāo)識(shí)參數(shù)表的開始,后跟參數(shù)表;

            3 、參數(shù)表以代號(hào)表示:

            X--void

            D--char

            E--unsigned char

            F--short

            H--int

            I--unsigned int

            J--long

            K--unsigned long

            M--float

            N--double

            _N--bool

            ....

            PA-- 表示指針,后面的代號(hào)表明指針類型,如果相同類型的指針連續(xù)出現(xiàn),以 "0" 代替,一個(gè) "0" 代表一次重復(fù);

            4 、參數(shù)表的第一項(xiàng)為該函數(shù)的返回值類型,其后依次為參數(shù)的數(shù)據(jù)類型 , 指針標(biāo)識(shí)在其所指數(shù)據(jù)類型前;

            5 、參數(shù)表后以 "@Z" 標(biāo)識(shí)整個(gè)名字的結(jié)束,如果該函數(shù)無參數(shù),則以 "Z" 標(biāo)識(shí)結(jié)束。

            其格式為 "?functionname@@YG*****@Z" "?functionname@@YG*XZ" ,例如

            ????????? int Test1 char *var1,unsigned long ----- ?Test1@@YGHPADK@Z

            ????????? void Test2 () ?????????????????????? ----- ?Test2@@YGXXZ

            ?

            __cdecl 調(diào)用約定:

            規(guī)則同上面的 _stdcall 調(diào)用約定,只是參數(shù)表的開始標(biāo)識(shí)由上面的 "@@YG" 變?yōu)?/span> "@@YA"

            __fastcall 調(diào)用約定:

            規(guī)則同上面的 _stdcall 調(diào)用約定,只是參數(shù)表的開始標(biāo)識(shí)由上面的 "@@YG" 變?yōu)?/span> "@@YI" VC++ 對(duì)函數(shù)的省缺聲明是 "__cedcl", 將只能被 C/C++ 調(diào)用 .

            ?

            ?? 通常我們希望我們的 DLL 中的導(dǎo)出函數(shù)名能夠更易被識(shí)別(用戶使用才會(huì)更方便),也就是說 DLL 應(yīng)該編譯出無修飾的 C 函數(shù)名,而不復(fù)雜的修飾名。所以當(dāng)使用 C++ 文件來創(chuàng)建 DLL 時(shí)應(yīng)該使用 extern “c” 來修飾導(dǎo)出函數(shù)和變量。實(shí)際上 VS 編譯器已經(jīng)為我們準(zhǔn)備了一個(gè)宏 EXTERN_C 用來代替 extern “c” 。修改以上的代碼如下:

            EXTERN_C TRY_API int nTry;

            EXTERN_C TRY_API int fnTry(void);

            生成 DLL ,用 depens 工具查看,他們已經(jīng)變成了如下模樣:

            ?

            這樣在客戶端就可以通過函數(shù)名去調(diào)用它們。但是 EXTERN_C 宏卻沒辦法修飾類成員函數(shù),

            如果像全局函數(shù)那樣修飾類成員函數(shù),編譯無法通過。用 .DEF 文件可以解決這個(gè)問題。

            ?

            關(guān)于 .def 文件

            模塊定義 (.def) 文件是包含一個(gè)或多個(gè)描述 DLL 各種屬性的 Module 語句的文本文件。

            def 文件包含下列模塊定義語句:

            1. 文件中的第一個(gè)語句必須是 LIBRARY 語句。此語句將 .def 文件標(biāo)識(shí)為屬于 DLL LIBRARY 語句的后面是 DLL 的名稱。鏈接器將此名稱放到 DLL 的導(dǎo)入庫(kù)中。

            2. ?EXPORTS 語句列出被導(dǎo)出函數(shù)的名字;將要輸出的函數(shù)修飾名羅列在 EXPORTS 之下,這個(gè)名字必須與定義函數(shù)的名字完全一致,如此就得到一個(gè)沒有任何修飾的函數(shù)名了。

            3.? 可以使用 DESCRIPTION 語句描述 DLL 的用途 ( 此句可選 )

            4.?? ";" 對(duì)一行進(jìn)行注釋 ( 可選 )

            ?

            創(chuàng)建一 .DEF 文件,命名為 export.def ,用來修飾 CTry::print 函數(shù),如下:

            LIBRARY??? "Try"

            EXPORTS

            print????? =????? ?print@CTry@@UAEXXZ? PRIVATE

            ?

            DLL 工程屬性 -> 鏈接器 -> 輸入 -> 模塊定義文件中將 export.def 添加進(jìn)去。這樣就可以直接通過函數(shù)名來顯式調(diào)用類成員函數(shù)了。下面是 DLL depens 工具中顯示的情況。

            ?

            參考資料:

            windows 核心編程》

            programming windows

            《微軟 DLL 專題》

            posted on 2009-03-08 17:01 隙中駒 閱讀(516) 評(píng)論(1)  編輯 收藏 引用

            FeedBack:
            # re: DLL學(xué)習(xí)筆記 3
            2009-03-12 09:32 | guest
            圖片的路徑是“e:/1.bmp”,你本地應(yīng)該可以顯示,但是其他地方都看不到圖了。
            請(qǐng)修改圖片鏈接。  回復(fù)  更多評(píng)論
              

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理



            <2009年3月>
            22232425262728
            1234567
            891011121314
            15161718192021
            22232425262728
            2930311234

            常用鏈接

            留言簿(2)

            隨筆檔案(4)

            文章分類(1)

            文章檔案(1)

            友情鏈接

            最新隨筆

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            亚洲精品NV久久久久久久久久 | 国产精品久久亚洲不卡动漫| 综合网日日天干夜夜久久| 午夜欧美精品久久久久久久| 免费国产99久久久香蕉| 国产农村妇女毛片精品久久 | 国产精品一久久香蕉国产线看观看 | 伊人久久大香线蕉亚洲| 成人久久精品一区二区三区| 亚洲综合久久夜AV | 久久国产精品99久久久久久老狼| 亚洲欧美一级久久精品| 国产精品99久久不卡| 久久综合久久自在自线精品自| 久久久艹| 国产成人精品久久亚洲高清不卡| 久久精品亚洲AV久久久无码| 国产99久久久国产精品~~牛| 99精品国产综合久久久久五月天| 国产福利电影一区二区三区久久久久成人精品综合 | 国产精品成人久久久久久久| 亚洲人AV永久一区二区三区久久| 亚洲国产成人久久综合一| 久久精品亚洲一区二区三区浴池 | 日本精品久久久久中文字幕| 亚洲伊人久久精品影院| 老司机午夜网站国内精品久久久久久久久 | 亚洲国产精品高清久久久| 欧美激情精品久久久久久久九九九 | 久久久青草久久久青草| 久久精品国产亚洲av麻豆小说| 久久久精品国产免大香伊 | 国产美女久久久| 久久精品欧美日韩精品| 久久精品国产精品亚洲毛片 | 精品伊人久久久| 色综合久久天天综线观看| 亚洲欧洲中文日韩久久AV乱码| 欧美激情精品久久久久久久| 一本久久综合亚洲鲁鲁五月天亚洲欧美一区二区 | 国产精品无码久久久久久|