• <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>
            白駒過隙
            學而不思則罔,思而不學則怠
            posts - 4,  comments - 3,  trackbacks - 0

            ?? 還有一種方法可以顯式導出類成員函數,就是采用虛函數表的方法。 COM 就是這樣做的。當在類中聲明一組虛函數的時候, 編譯器會創建一個虛函數表,將各虛函數的地址按聲明的順序放入其中。當一個類對象被創建時,它的前四個字節是一個指針,指向這個虛函數表。所以,修改以上的代碼:

            //CTry.h

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

            TRY_API CTry* createObject() ; // 聲明一個全局函數,創建一個 Ctry 對象的實例

            //CTry.cpp

            CTry *createObject()

            {

            ??? return new CTry() ;

            }

            ?

            在測試工程中,顯式導出該全局函數,并通過它調用類成員函數。

            typedef CTry* (*Fn_FunType)() ;

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

            CTry * pTry = pFun() ;

            pTry ->print() ;

            delete pTry ;


            ?? 這個方法雖然同樣能達到顯式導出類成員函數的目的。但是上面的方法有個缺點,就是在DLLnew 出來的對象,卻要放到客戶端釋放。因為客戶端并不知道DLL中的實現方法,很可能會不知道釋放這個對象,從而造成內存泄露。<<Effiective C++>>中明文規定禁止這樣做。

            ?

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


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

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

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

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

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

            ????
            // 不允許通過構造函數初始化對象
            ????CTry( void )?;
            ????
            ????
            virtual ? ~ CTry()?;


            ????
            // 不允許相互賦值,這里面細節太多,懶得去分析: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?應用程序的入口點。
            //

            #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

            // ?這是導出變量的一個示例
            TRY_API? int ?nTry? = ? 0 ;

            // ?這是導出函數的一個示例。
            TRY_API? int ?fnTry( void )
            {
            ????
            return ? 42 ;
            }


            // ?這是已導出類的構造函數。
            // ?有關類定義的信息,請參閱?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;
            ?}

            ?

            ? 上面的方法中,用戶不能通過構造函數來實例化一個對象,必須通過 createObject 接口才能實例化對象,當用戶使用完這個對象時必須調用 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 ?;
            }



            ? 當然上面只是我為了方便測試而寫的一個簡單的例子,還不完善。引用計數還有很多細節和注意事項。具體請看《more effective c++》第29項。

            ?

            關于名字修飾約定

            ?

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

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

            A C 編譯時函數名修飾約定規則:

            __stdcall 調用約定在輸出函數名前加上一個下劃線前綴,后面加上一個 "@" 符號和其參數的字節數,格式為 _functionname@number

            __cdecl 調用約定僅在輸出函數名前加上一個下劃線前綴,格式為 _functionname

            __fastcall 調用約定在輸出函數名前加上一個 "@" 符號,后面也是一個 "@" 符號和其參數的字節數,格式為 @functionname@number

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

            B C++ 編譯時函數名修飾約定規則:

            __stdcall 調用約定:

            1 、以 "?" 標識函數名的開始,后跟函數名;

            2 、函數名后面以 "@@YG" 標識參數表的開始,后跟參數表;

            3 、參數表以代號表示:

            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-- 表示指針,后面的代號表明指針類型,如果相同類型的指針連續出現,以 "0" 代替,一個 "0" 代表一次重復;

            4 、參數表的第一項為該函數的返回值類型,其后依次為參數的數據類型 , 指針標識在其所指數據類型前;

            5 、參數表后以 "@Z" 標識整個名字的結束,如果該函數無參數,則以 "Z" 標識結束。

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

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

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

            ?

            __cdecl 調用約定:

            規則同上面的 _stdcall 調用約定,只是參數表的開始標識由上面的 "@@YG" 變為 "@@YA"

            __fastcall 調用約定:

            規則同上面的 _stdcall 調用約定,只是參數表的開始標識由上面的 "@@YG" 變為 "@@YI" VC++ 對函數的省缺聲明是 "__cedcl", 將只能被 C/C++ 調用 .

            ?

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

            EXTERN_C TRY_API int nTry;

            EXTERN_C TRY_API int fnTry(void);

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

            ?

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

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

            ?

            關于 .def 文件

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

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

            1. 文件中的第一個語句必須是 LIBRARY 語句。此語句將 .def 文件標識為屬于 DLL LIBRARY 語句的后面是 DLL 的名稱。鏈接器將此名稱放到 DLL 的導入庫中。

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

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

            4.?? ";" 對一行進行注釋 ( 可選 )

            ?

            創建一 .DEF 文件,命名為 export.def ,用來修飾 CTry::print 函數,如下:

            LIBRARY??? "Try"

            EXPORTS

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

            ?

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

            ?

            參考資料:

            windows 核心編程》

            programming windows

            《微軟 DLL 專題》

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

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

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(2)

            隨筆檔案(4)

            文章分類(1)

            文章檔案(1)

            友情鏈接

            最新隨筆

            搜索

            •  

            最新評論

            • 1.?re: DLL學習筆記 3
            • 圖片的路徑是“e:/1.bmp”,你本地應該可以顯示,但是其他地方都看不到圖了。
              請修改圖片鏈接。
            • --guest
            • 2.?re: DLL學習筆記2
            • 圖片的路徑是“e:/1.bmp”,你本地應該可以顯示,但是其他地方都看不到圖了。
              請修改下圖片鏈接。
            • --guest
            • 3.?re: DLL學習筆記1
            • 好,我跟著你一起學習
            • --bk

            閱讀排行榜

            評論排行榜

            久久精品一本到99热免费| 久久精品国产黑森林| 中文精品久久久久人妻| 久久亚洲电影| 亚洲精品乱码久久久久久蜜桃不卡| 日韩精品无码久久一区二区三| 伊人久久大香线焦综合四虎| 日本精品久久久久中文字幕| 亚洲国产精品成人AV无码久久综合影院 | 婷婷综合久久中文字幕蜜桃三电影| 亚洲国产另类久久久精品小说 | 久久国产乱子伦精品免费午夜| 久久久久久亚洲精品不卡| 精品人妻伦九区久久AAA片69| 91久久婷婷国产综合精品青草 | 亚洲AⅤ优女AV综合久久久| 久久发布国产伦子伦精品 | 亚洲午夜久久久| 久久嫩草影院免费看夜色| 国产精品久久亚洲不卡动漫| 伊人色综合久久天天网| 久久综合五月丁香久久激情| 久久国产亚洲高清观看| 久久亚洲中文字幕精品有坂深雪| 亚洲国产成人久久一区久久| 亚洲欧美久久久久9999| 久久久久国产视频电影| 久久久亚洲裙底偷窥综合| 久久免费大片| 久久中文字幕人妻丝袜| 天天躁日日躁狠狠久久| 99久久精品国产高清一区二区| 久久精品国产亚洲沈樵| 久久久黄色大片| 国产精品99久久久久久人| 久久久青草青青国产亚洲免观| 久久99热这里只有精品66| 国产精品久久久久久吹潮| 久久久久国色AV免费看图片| 久久亚洲日韩精品一区二区三区| 国内精品久久久久伊人av|