• <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>

            山寨:不是最好的,是最適合我們的!歡迎體驗(yàn)山寨 中文版MSDN

            Blog @ Blog

            當(dāng)華美的葉片落盡,生命的脈絡(luò)才歷歷可見。 -- 聶魯達(dá)

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            BBS

            Blog

            Web

            最新評論

            VC之DLL編程

            DLL編程

            靜態(tài)鏈接:每個(gè)應(yīng)用程序使用函數(shù)庫,必須擁有一份庫的備份。多個(gè)應(yīng)用程序運(yùn)行時(shí),內(nèi)存中就有多份函數(shù)庫代碼的備份。

            動態(tài)連接庫:多個(gè)應(yīng)用程序可以共享一份函數(shù)庫的備份。

             

            DLL的調(diào)用方式:DLL的使用者在調(diào)用庫中輸出函數(shù)時(shí),函數(shù)參數(shù)的壓棧和出棧順序和方法。

            VC++支持四種方式:

            <1>_cdecl調(diào)用方式: 也叫C調(diào)用方式,函數(shù)參數(shù)的壓棧順序是從右至左,參數(shù)的出棧方式由調(diào)用者完成,在調(diào)用DLL的函數(shù)的地方都應(yīng)包含清空堆棧的代碼,它是C/C++缺省的調(diào)用方式。

            <2>_stdcall標(biāo)準(zhǔn)調(diào)用方式:函數(shù)參數(shù)壓棧順序是從右至左,參數(shù)出棧工作由被調(diào)用者負(fù)責(zé)完成。系統(tǒng)將加在函數(shù)原型定義前的”WINAPI”宏翻譯為適當(dāng)?shù)恼{(diào)用方式,對于Win32_stdcall調(diào)用方式。

            <3>_fastcall:主要特點(diǎn)是調(diào)用速度快,被調(diào)用的函數(shù)參數(shù)傳遞不依靠堆棧,而是通過寄存器,但并不是對所有的參數(shù)傳遞均使用寄存器,往往只是用ECXEDX傳送前兩個(gè)雙字或比較小的參數(shù),其余的參數(shù)傳遞仍然采用從右至左壓棧方式,出棧工作由被調(diào)用的函數(shù)完成。

            <4>thiscall:前三種是關(guān)鍵字,可以加到函數(shù)前作修飾,thiscall不是關(guān)鍵字,因此程序中不能顯式寫入,這種方式僅應(yīng)用于C++成員函數(shù),thisC++中指向?qū)ο蟮闹羔槪?span lang=EN-US>this存放在ECX寄存器中,參數(shù)從右至左壓棧,出棧由被調(diào)用者完成。

             

            DLL的入口函數(shù)

            DllMain()函數(shù)負(fù)責(zé)完成DLL的初始化和解說DLL調(diào)用后的清理工作。當(dāng)加載DLL時(shí),如存在DllMain()函數(shù)則調(diào)用它。

             

            MFC DLL

              MFC DLL可以讓我們的程序使用MFC庫,它分為3類:

            <1>Regular Dll with MFC Statically linked   (正規(guī))

                 靜態(tài)鏈接MFC庫,在DLL工程中將包含工程中所需的MFC庫代碼的拷貝,因此,程序可以脫離MFC庫使用。

            <2>Regular Dll using shared MFC Dll        (正規(guī))

                 動態(tài)共享MFC庫,工程必須在裝有MFC庫的機(jī)器上才能運(yùn)行。

            MFC正規(guī)DLL編寫注意問題:

                 應(yīng)在輸出函數(shù)的函數(shù)體內(nèi)首先加入:

                 AFX_MANAGE_STATE(AfxGetStaticModuleState());

            <3>MFC Extension Dll(using shared MFC Dll)     (擴(kuò)展)

                 它不但能輸出函數(shù),還能輸出類,用戶可以直接使用、繼承這個(gè)輸出類,但它不是正規(guī)DLL,如果要讓非VC++程序調(diào)用,必須使用正規(guī)DLL

            MFC擴(kuò)展DLL編寫注意問題:

                 <1>在要輸出的類定義中加入:AFC_EXT_CLASS

                     如:class AFX_EXT_CLASS  cls{};

                 <2>在要輸出的函數(shù)定義前加入:AFC_EXT_CLASS

                     如:AFX_EXT_CLASS int  func(){}

             

            自行編寫DLL的方法

                <1>DLL中編寫的函數(shù)前加上”__declspec(dllexport)”即可導(dǎo)出該函數(shù)。

            <2>DLL中導(dǎo)出類,是在class和類名之間加入”__declspec(dllexport)”,如果只想導(dǎo)出類中指定的函數(shù),可只在該函數(shù)前加上”__declspec(dllexport)”

            <3>C++為支持函數(shù)重載,采用了名稱壓軋,因此,DLL文件在編譯時(shí),函數(shù)名會發(fā)生改變,為保證對DLL的正確訪問,可在”__declspec(dllexport)”聲明之前加入”extern “c””,編譯時(shí)就不會發(fā)生名稱改變,但extern “c”只能用于導(dǎo)出全局函數(shù),不能導(dǎo)出類的成員,如果在函數(shù)名前加入了調(diào)用約定(如:_stdcall),編譯時(shí)還是會發(fā)生名稱改變。

                     也可通過模塊定義文件的方式解決名稱改變的問題,模塊定義文件的后綴為”.def”,步驟如下:

                     1新建一個(gè)文本文檔,改名為”x.def”

                     2x.def加入到工程;

            3編輯x.defEXPORTS下所寫函數(shù)名如與DLL文件中函數(shù)名相同,則以所寫名稱導(dǎo)出該函數(shù)。

                <4>DLL通過GetForegroundWindow()獲得正在使用它的前景窗口的句柄。

                <5>GetModuleHandle()得到一個(gè)DLL的句柄。

                    GetSystemMetrics()獲取系統(tǒng)信息。

             

            系統(tǒng)對DLL中可改變的數(shù)據(jù),在進(jìn)程寫訪問時(shí)會拷貝到一個(gè)新的數(shù)據(jù)頁面,如果多個(gè)進(jìn)程要共享該數(shù)據(jù),可設(shè)置節(jié),創(chuàng)建節(jié)后,將數(shù)據(jù)放到節(jié)中且必須初始化:

            #pragma     data_seg(“name”)          //開頭

                //數(shù)據(jù)

            #pragma     data_seg()      //結(jié)尾

            #pragma     comment(linker,”/section:name,RWS”)

                //設(shè)為讀、寫、共享,也可寫在.def文件中。

             

            使用DLL

                要使用DLL,首先要將DLL文件映像到用戶進(jìn)程的地址空間中,并聲明被調(diào)用的函數(shù),然后才能進(jìn)行函數(shù)調(diào)用,調(diào)用方法與一般函數(shù)相同。

                DLL映像到進(jìn)程地址空間的方法:

                <1>隱式的加載時(shí)鏈接

                    DLL工程經(jīng)編譯后,產(chǎn)生一個(gè).dll文件,一個(gè).lib文件及一個(gè)包含DLL輸出函數(shù)聲明的.h頭文件,隱式調(diào)用DLL就是將這個(gè).lib文件鏈接到工程中。

                    lib文件中包含了DLL允許調(diào)用的所有函數(shù)列表,鏈接器發(fā)現(xiàn)程序調(diào)用了lib文件中列出的某個(gè)函數(shù)時(shí),會在程序的可執(zhí)行文件的文件映像中加入包含這個(gè)函數(shù)的DLL文件的名字信息,當(dāng)程序運(yùn)行時(shí),可執(zhí)行文件被操作系統(tǒng)產(chǎn)生映像文件,系統(tǒng)會查看這個(gè)映像文件中關(guān)于DLL的信息,然后將這個(gè)DLL文件映像到進(jìn)程的地址空間。

                鏈接lib文件的方法:

            1加入到文件列表

            2Link項(xiàng)下加入

            3#pragma   comment(lib,”mydll.lib”)

            <2>顯式的運(yùn)行時(shí)鏈接步驟如下:

            1LoadLibrary()AfxLoadLibrary()加載DLL或可運(yùn)行模塊;

            2GetProcAddress()得到要調(diào)用的DLL中函數(shù)的指針,然后使用該函數(shù);

            3使用完DLL以后,用FreeLibrary()DLL在進(jìn)程的映射解除,減少加載DLL的記數(shù)。

                被調(diào)用的函數(shù)聲明的方法有三種:

                <1>”extern”聲明被調(diào)用函數(shù)。

                <2>使用”__declspec(dllimport)”聲明,即告訴編譯器,所引用的函數(shù)或文件是從DLL中輸入的,編譯器能生成運(yùn)行效率更高的代碼。

                <3>也可將聲明放在DLL編寫的頭文件中,在使用的文件中包含該頭文件即可。

             

             

            要使用DLL中導(dǎo)出的類,必須在使用的文件中包含該類所在的頭文件!

            盡量導(dǎo)出方法(做接口)少導(dǎo)出類。

            標(biāo)準(zhǔn)DLL中導(dǎo)出函數(shù)的寫法:

            extern "C"  BOOL __declspec(dllexport) EXPORT ShowDlg()   ///標(biāo)準(zhǔn)導(dǎo)出函數(shù)格式

            {

                AFX_MANAGE_STATE(AfxGetStaticModuleState());//些宏不可少!

                // 此處為普通函數(shù)體

            }

            1.AfxGetStaticModuleState()指向當(dāng)前模塊狀態(tài);

            2.當(dāng)前函數(shù)調(diào)用結(jié)束后原模塊的狀態(tài)自動被恢復(fù);

            3.用于DLL中所調(diào)用MFC函數(shù)、類、資源時(shí)的模塊狀態(tài)切換

            并在def文件中定義導(dǎo)出函數(shù)的序號:ShowDlg @1

            在調(diào)用處寫如下代碼:

            typedef void (*dllfun)(); //定義函數(shù)指針類型

            HINSTANCE   hlib= NULL;

            hlib=LoadLibrary("std_dll.dll"); //加載庫

            dllfun  ShowDlg  = NULL;//定義函數(shù)指針

            ShowDlg=(dllfun)GetProcAddress(hlib,"ShowDlg");//獲取庫中函數(shù)地址

            ShowDlg();  //調(diào)用函數(shù)

             

             

            擴(kuò)展DLL導(dǎo)出類的寫法:

            class AFX_EXT_CLASS  clsName{};

            擴(kuò)展DLL中的資源使用

            簡單的說:每個(gè)DLL有自己特有的資源。在使用時(shí),明確的告訴系統(tǒng)要使用哪個(gè)DLL的資源。現(xiàn)在的問題就是如何告訴系統(tǒng)使用哪個(gè)DLL的資源。函數(shù):AfxSetResourceHandle() 可以完成這個(gè)功能。參數(shù)是資源的句柄。

            那怎么得到某個(gè)DLL的資源句柄呢?如下:

            在擴(kuò)展DLL的入口函數(shù)

            extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)

             {

            if (!AfxInitExtensionModule(ShpSymbolDLL, hInstance))

                   return 0;

            }

            其中ShpSymbolDll 可能會因工程名不同而不同,這里就以這個(gè)名稱代替來說明了,DLL的資源句柄就可以在此得到。

             ShpSymbolDLL 定義:

             AFX_EXTENSION_MODULE ShpSymbolDLL  = { NULL, NULL };

            ShpSymbolDLL.hResource 這個(gè)就是我們要的了。其它參數(shù)請看說明。現(xiàn)在我們在使用某DLL的資源時(shí)只要先加入以下兩行就可以正確執(zhí)行了:

            HINSTANCE hOld = AfxGetResourceHandle();

            AfxSetResourceHandle( ShpSymbolDLL.hResource );

            注意在用完之后再恢復(fù):

            AfxSetResourceHandle( hOld );

            另外一個(gè)不得不提起的東西,在入口函數(shù)中有一行

            new CDynLinkLibrary(ShpSymbolDLL);

            旁邊有一說明:將此 DLL 插入到資源鏈中。

            的確如此。言下之意,上面所說的沒什么用了?其實(shí)不然,假如DLL中有一個(gè)Dialog ID120,在你調(diào)用此DLL的應(yīng)用程序資源中,如果沒有ID的值為120。那么,上面的都是白做了,你會得到預(yù)料中的結(jié)果。但如果應(yīng)用程序中有一相同ID的對話框資源呢?請大家一試。結(jié)果就不一樣了。其中的原因與new CDynLinkLibrary(ShpSymbolDLL) 相關(guān)聯(lián)。

            具體請看MFC中 的代碼 DoModal() 就會得到解答。或看MSDN中帶的例子 dllhusk,系統(tǒng)自動會查找相應(yīng)的資源,但不會判斷哪個(gè)是正確的。以找到的第一個(gè)資源為準(zhǔn)。

            另:為了編寫方便,可以寫一個(gè)類 ,寫成全局的。

            class CModuleInfo

            {public:

               HMODULE    m_hModule;

               HMODULE    m_hResource;

             public:

               CModuleInfo(void){}

               ~CModuleInfo(void){}

             };

            class AFX_EXT_CLASS CModuleStateMana

            {

               HINSTANCE m_hInstOld;

             public:

               CModuleStateMana();

               ~CModuleStateMana();

            };

            實(shí)現(xiàn)如下:

             CModuleInfo  s_mi;

             CModuleStateMana::CModuleStateMana()

            {

               m_hInstOld = AfxGetResourceHandle();

               AfxSetResourceHandle( s_mi.m_hModule );

             }

            CModuleStateMana::~CModuleStateMana()

            {

               AfxSetResourceHandle( m_hInstOld );

            }

            然后在入口函數(shù)之前加入

            extern CModuleInfo s_mi;

            函數(shù)中加入:

                 s_mi.m_hModule   = ShpSymbolDLL.hModule;

                 s_mi.m_hResource= ShpSymbolDLL.hResource;

            在調(diào)用的時(shí)候只要先加入:

             CModuleStateMana msm;

            就可以正確調(diào)用了。

            posted on 2008-06-20 14:07 isabc 閱讀(896) 評論(0)  編輯 收藏 引用 所屬分類: C++基礎(chǔ)

            廣告信息(免費(fèi)廣告聯(lián)系)

            中文版MSDN:
            歡迎體驗(yàn)

            国产亚洲美女精品久久久| 婷婷五月深深久久精品| AA级片免费看视频久久| 亚洲国产成人久久综合野外| 国内精品久久久久影院老司| www性久久久com| 婷婷久久综合九色综合绿巨人| 蜜臀av性久久久久蜜臀aⅴ| 精品久久久久一区二区三区 | 久久亚洲私人国产精品| 91麻精品国产91久久久久| 久久婷婷五月综合国产尤物app | 久久久久久亚洲精品成人| 精品无码久久久久久久久久| 无码超乳爆乳中文字幕久久| 看全色黄大色大片免费久久久| 97久久精品国产精品青草| 国产A级毛片久久久精品毛片| 国产呻吟久久久久久久92| 久久亚洲精品成人av无码网站| 伊人久久无码精品中文字幕| 国产精品久久久99| 精品综合久久久久久97超人| 伊人久久大香线蕉av一区| 亚洲国产成人久久一区WWW| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 一本大道久久香蕉成人网| 国产精品美女久久久久av爽 | 欧美久久久久久午夜精品| 久久青青草原国产精品免费| 久久人人爽人人爽人人片AV不| 久久精品国产亚洲AV不卡| 日韩电影久久久被窝网| 久久精品亚洲男人的天堂| 99久久精品国产综合一区| 久久不射电影网| 久久亚洲国产午夜精品理论片 | 亚洲综合久久夜AV | 亚洲国产精品狼友中文久久久 | 偷偷做久久久久网站| 一本色道久久综合狠狠躁篇|