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

            dll基礎(chǔ)

            Posted on 2008-07-29 16:08 RichardHe 閱讀(307) 評(píng)論(0)  編輯 收藏 引用 所屬分類: [再轉(zhuǎn)]
            自從微軟推出第一個(gè)版本的Windows操作系統(tǒng)以來(lái),動(dòng)態(tài)鏈接庫(kù)(DLL)一直是Wind
            ows操作系統(tǒng)的基礎(chǔ)。
            動(dòng)態(tài)鏈接庫(kù)通常都不能直接運(yùn)行,也不能接收消息。它們是一些獨(dú)立的文件,其中
            包含能被可執(zhí)行程序或其它DLL調(diào)用來(lái)完成某項(xiàng)工作的函數(shù)。只有在其它模塊調(diào)用動(dòng)
            態(tài)鏈接庫(kù)中的函數(shù)時(shí),它才發(fā)揮作用。
            Windows API中的所有函數(shù)都包含在DLL中。其中有3個(gè)最重要的DLL,Kernel32.dll
            ,它包含用于管理內(nèi)存、進(jìn)程和線程的各個(gè)函數(shù);User32.dll,它包含用于執(zhí)行用
            戶界面任務(wù)(如窗口的創(chuàng)建和消息的傳送)的各個(gè)函數(shù);GDI32.dll,它包含用于畫(huà)
            圖和顯示文本的各個(gè)函數(shù)。
                       靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
            靜態(tài)庫(kù):函數(shù)和數(shù)據(jù)被編譯進(jìn)一個(gè)二進(jìn)制文件(通常擴(kuò)展名為.LIB)。在使用靜態(tài)庫(kù)
            的情況下,在編譯鏈接可執(zhí)行文件時(shí),鏈接器從庫(kù)中復(fù)制這些函數(shù)和數(shù)據(jù)并把它們
            和應(yīng)用程序的其它模塊組合起來(lái)創(chuàng)建最終的可執(zhí)行文件(.EXE文件)。
            在使用動(dòng)態(tài)庫(kù)的時(shí)候,往往提供兩個(gè)文件:一個(gè)引入庫(kù)和一個(gè)DLL。引入庫(kù)包含被D
            LL導(dǎo)出的函數(shù)和變量的符號(hào)名,DLL包含實(shí)際的函數(shù)和數(shù)據(jù)。在編譯鏈接可執(zhí)行文件
            時(shí),只需要鏈接引入庫(kù),DLL中的函數(shù)代碼和數(shù)據(jù)并不復(fù)制到可執(zhí)行文件中,在運(yùn)行
            的時(shí)候,再去加載DLL,訪問(wèn)DLL中導(dǎo)出的函數(shù)。
            使用動(dòng)態(tài)鏈接庫(kù)的好處
            可以采用多種編程語(yǔ)言來(lái)編寫(xiě)。
            增強(qiáng)產(chǎn)品的功能。
            提供二次開(kāi)發(fā)的平臺(tái)。
            簡(jiǎn)化項(xiàng)目管理。
            可以節(jié)省磁盤(pán)空間和內(nèi)存。
            有助于資源的共享。
            有助于實(shí)現(xiàn)應(yīng)用程序的本地化。
            動(dòng)態(tài)鏈接庫(kù)被多個(gè)進(jìn)程訪問(wèn)
            動(dòng)態(tài)鏈接庫(kù)加載的兩種方式
            預(yù)備知識(shí)
            ::::::::::::::::::::::::::::::::::::
            ::::::::::::::::::::::::::::::::::::
            ::::
            進(jìn)程的虛擬地址空間:
             在32為系統(tǒng)中,系統(tǒng)為沒(méi)個(gè)進(jìn)程分配2^32的地址空間
            具體可參看《windows核心編程》
            預(yù)處理命令:
            沒(méi)有什么可以說(shuō)的了
            看看代碼就明白了!!
            環(huán)境變量:(環(huán)境變量的概念我就不介紹了,具體的可以參看windows 核心編程,
            上面有很詳細(xì)的說(shuō)明)把DLL放到當(dāng)前任意的環(huán)境變量中就可以加載
            定義函數(shù)指針
            格式:typedef  int (*proc)(int a,int b);
            注意:proc是一個(gè)函數(shù)指針類型而不是一個(gè)變量
            然后我們可以用這個(gè)指針類型去定義變量
            Proc myproc;這里的myproc就是一個(gè)指針變量
            要是實(shí)在無(wú)聊 內(nèi)心空虛 沒(méi)事可做的話 可以去這個(gè)網(wǎng)站看看函數(shù)的指針http://uf
            o.tyedu.com/study/programmer/language_C/200412/1472.html  
            函數(shù)的調(diào)用約定(可以不必了解,但是理解后可以讓你理解DLL的調(diào)用更為深刻)
            函數(shù)的調(diào)用約定:函數(shù)調(diào)用約定是函數(shù)調(diào)用者和被調(diào)用的函數(shù)體之間關(guān)于參數(shù)傳遞
            、返回值傳遞、堆棧清除、寄存器使用的一種約定;
               它是需要二進(jìn)制級(jí)別兼容的強(qiáng)約定,函數(shù)調(diào)用者和函數(shù)體如果使用不同的調(diào)用約
            定,將可能造成程序執(zhí)行錯(cuò)誤,必須把它看作是函數(shù)聲明的一部分;
            常見(jiàn)的函數(shù)調(diào)用約定:
            VC6中的函數(shù)調(diào)用約定:
                   調(diào)用約定        堆棧清除    參數(shù)傳遞
                   __cdecl         調(diào)用者      從右到左,通過(guò)堆棧傳遞
                   __stdcall       函數(shù)體      從右到左,通過(guò)堆棧傳遞
                   __fastcall      函數(shù)體      從右到左,優(yōu)先使用寄存器(ECX,EDX),然后
            使用堆棧
                   thiscall        函數(shù)體      this指針默認(rèn)通過(guò)ECX傳遞,其它參數(shù)從右
            到左入棧
            __cdecl是C\C++的默認(rèn)調(diào)用約定; VC的調(diào)用約定中并沒(méi)有thiscall這個(gè)關(guān)鍵字,它是
            類成員函數(shù)默認(rèn)調(diào)用約定;
            C\C++中的main(或wmain)函數(shù)的調(diào)用約定必須是__cdecl,不允許更改;
            默認(rèn)調(diào)用約定一般能夠通過(guò)編譯器設(shè)置進(jìn)行更改,如果你的代碼依賴于調(diào)用約定,請(qǐng)
            明確指出需要使用的調(diào)用約定;
            常見(jiàn)的函數(shù)調(diào)用約定中,只有cdecl約定需要調(diào)用者來(lái)清除堆棧;
            C\C++中的函數(shù)支持參數(shù)數(shù)目不定的參數(shù)列表,比如printf函數(shù);由于函數(shù)體不知道調(diào)
            用者在堆棧中壓入了多少參數(shù),
            所以函數(shù)體不能方便的知道應(yīng)該怎樣清除堆棧,那么最好的辦法就是把清除堆棧的責(zé)
            任交給調(diào)用者;
            這應(yīng)該就是cdecl調(diào)用約定存在的原因吧;
            VB一般使用的是stdcall調(diào)用約定;(ps:有更強(qiáng)的保證嗎)
            Windows的API中,一般使用的是stdcall約定;(ps: 有更強(qiáng)的保證嗎)
            建議在不同語(yǔ)言間的調(diào)用中(如DLL)最好采用stdcall調(diào)用約定,因?yàn)樗谡Z(yǔ)言間兼容
            性支持最好;
            三:函數(shù)返回值傳遞方式
              其實(shí),返回值的傳遞從處理上也可以想象為函數(shù)調(diào)用的一個(gè)out形參數(shù); 函數(shù)返
            回值傳遞方式也是函數(shù)調(diào)用約定的一部分;
              有返回值的函數(shù)返回時(shí):一般int、指針等32bit數(shù)據(jù)值(包括32bit結(jié)構(gòu))通過(guò)ea
            x傳遞,(bool,char通過(guò)al傳遞,short通過(guò) ax傳遞),特別的__int64等64bit結(jié)構(gòu)(
            struct) 通過(guò)edx,eax兩個(gè)寄存器來(lái)傳遞(同理:32bit整形在16bit環(huán)境中通過(guò)dx,a
            x傳遞); 其他大小的結(jié)構(gòu)(struct)返回時(shí)把其地址通過(guò)eax返回;(所以返回值類型
            不是1,2,4,8byte時(shí),效率可能比較差)
              參數(shù)和返回值傳遞中,引用方式的類型可以看作與傳遞指針?lè)绞较嗤?
              float\double(包括Delphi中的extended)都是通過(guò)浮點(diǎn)寄存器st(0)返回;

            具體的分析參看:http://blog.csdn.net/avalonbbs/archive/2004/12/25/229300
            .aspx
            ::::::::::::::::::::::::::::::::::::
            ::::::::::::::::::::::::::::::::::::
            :::::::




            隱式鏈接
            本文現(xiàn)在對(duì)隱式鏈接不作具體的說(shuō)明,只是做一個(gè)大概的介紹(下次再做具體的說(shuō)
            明).當(dāng)進(jìn)程運(yùn)行的時(shí)候,所有的相關(guān)的DLL都被加載到內(nèi)存,然后映射到進(jìn)程的地
            址空間,當(dāng)一個(gè)進(jìn)程要調(diào)用很多個(gè)DLL的時(shí)候,這種方法就顯得特別浪費(fèi)內(nèi)存,所以
            在加載很多個(gè)DLL的時(shí)候,最好用顯示加載的方式
            在調(diào)用DLL里面的函數(shù)時(shí)候
            要用extern 聲明是外部變量
            比如 extern int add(int num1,int num2);
            但是還應(yīng)該注意的就是:在編譯DLL時(shí),要把編譯的LIB文件放到執(zhí)行文件的目錄下
            ,并且在編譯執(zhí)行文件的時(shí)候要連接LIB文件。
            在寫(xiě)DLL的時(shí)候要導(dǎo)出的函數(shù)也就是在能被外部程序調(diào)用的函數(shù)前面加上
            一般大型項(xiàng)目在開(kāi)發(fā)DLL中,要進(jìn)行預(yù)定義聲明的

            ====================================
            ==
            在定義DLL的時(shí)候要定義導(dǎo)出函數(shù)就要在該函數(shù)前面加__declspec(DLLexport)時(shí),
            C++編譯器為了支持函數(shù)的重載會(huì)進(jìn)行函數(shù)名字改編,當(dāng)可執(zhí)行模塊執(zhí)行該函數(shù)時(shí)由
            于找不到該函數(shù)的名字,于是調(diào)用就會(huì)出現(xiàn)錯(cuò)誤!當(dāng)使用extern “C”時(shí)就可以告
            訴編譯器不必修改函數(shù)名和變量名。這樣就可以用C++或C編譯器程序調(diào)用該函數(shù)。

            以上操作只有在VC++創(chuàng)建的的可執(zhí)行模塊來(lái)調(diào)用該DLL,如果使用其他的編譯器的模
            塊來(lái)調(diào)用該DLL,就需要執(zhí)行一些額外的操作。
            C編譯在進(jìn)行編譯的時(shí)候也會(huì)進(jìn)行名字的改編,當(dāng)函數(shù)使用_stdcall(WINAPI)調(diào)用
            規(guī)則時(shí),MS編譯器就會(huì)改編函數(shù)的名稱。
            比如:__declspec(DLLexport)  LONG __stdcall  Proc(int a ,int b);
            編譯器會(huì)改編為_(kāi)_Proc@8
            因此 當(dāng)非C++或非C編譯器調(diào)用該DLL中的函數(shù)Proc時(shí),就會(huì)出現(xiàn)找不到函數(shù)的情況

            這樣我們就可以定義DEF文件來(lái)解決,并且在DEF文件加上下面的EXPORTS:
            EXPORTS
                   Proc
            Def模塊執(zhí)行原理:當(dāng)連接程序分析這個(gè)DEF文件時(shí),它發(fā)現(xiàn)Proc和__Proc@8都被輸
            出,由于這兩個(gè)函數(shù)是相互匹配的,因此連接程序使用Proc來(lái)輸出該函數(shù),根本不
            使用__Proc@8來(lái)輸出函數(shù)名
            ====================================
            ===

                            




            下面是def的具體使用方法

            ------------------------------------------------------------------------
            ----------------------------------------------模塊定義文件(.DEF)是一個(gè)或
            多個(gè)用于描述DLL屬性的模塊語(yǔ)句組成的文本文件,每個(gè)DEF文件至少必須包含以下
            模塊定義語(yǔ)句:
            * 第一個(gè)語(yǔ)句必須是LIBRARY語(yǔ)句,指出DLL的名字;
            * EXPORTS語(yǔ)句列出被導(dǎo)出函數(shù)的名字;將要輸出的函數(shù)修飾名羅列在EXPORTS之下
            ,這
            個(gè)名字必須與定義函數(shù)的名字完全一致,如此就得到一個(gè)沒(méi)有任何修飾的函數(shù)名了

            * 可以使用DESCRIPTION語(yǔ)句描述DLL的用途(此句可選);
            * ";"對(duì)一行進(jìn)行注釋(可選)。
            ------------------------------------------------------------------------
            ----------------------------------------------


            ////////////////////////////////////////////////////////////////////////
            ////////////////////////////////////////////////////////////////////////
            dlltest.h
            #ifdef DLL1_API
            #else
            #define DLL1_API extern "C" _declspec(dllimport)
            #endif

            DLL1_API int _stdcall add(int a,int b);
            DLL1_API int _stdcall subtract(int a,int b);
            ////////////////////////////////////////////////////////////////////////
            //////////////////////////////////////////////////////////////////////


            \\\\\\\\\\\\\\\\\\
            \\\\\\\\\\\\\\\\\\//
            dlltest.cpp
            #define DLL1_API extern "C" _declspec(dllexport)
            #include "Dll1.h"
            #include <stdio.h>
            int _stdcall add(int a,int b)
            {
                return a+b;
            }

            int _stdcall subtract(int a,int b)
            {
                return a-b;
            }
            \\\\\\\\\\\\\\\\\\
            \\\\\\\\\\\\\\\\\\

            ////////////////////////////////////////////////////////////////////////
            ////////////////////////////////////////////////////////////////////
            // def文件
            LIBRARY dlltest
            EXPORTS
            add
            subtract
            ////////////////////////////////////////////////////////////////////////
            ////////////////////////////////////////////////////////////////////

            有了上面的那些文件之后就可以在如何地方調(diào)用這些函數(shù)了

            void CDllTestDlg::OnBtnSubtract()
            {
                // TODO: Add your control notification handler code here
                CString str;
                str.Format("5-3=%d",subtract(5,3));
                MessageBox(str);
            }

            void CDllTestDlg::OnBtnOutput()
            {
                // TODO: Add your control notification handler code here
                Point pt;
                pt.output(5,3);
            }下面具體介紹顯示加載
            顯示加載
            VC++編譯器在編譯DLL的時(shí)候函數(shù)會(huì)發(fā)生名字改編;主要在非C++環(huán)境中就不能識(shí)別
            該函數(shù)了,所以這里應(yīng)該定義模塊文件類型DEF,主要就方便了非C++程序可以調(diào)用
            該DLL里面的函數(shù)
            再使用顯示加載前必須要注意的是名字的改編問(wèn)題,因?yàn)樵賱?dòng)態(tài)加載中名字改編后
            在加載就得不原來(lái)的函數(shù)名字了,這樣加載就會(huì)失敗。但是可以用另外一種方法加
            載:MSDN上對(duì)GetProAddress中的第二個(gè)參數(shù)是這樣說(shuō)明的Pointer to a null-ter
            minated string that specifies the function or variable name, or the func
            tion's ordinal value.也就是說(shuō)可以使用函數(shù)的序號(hào)來(lái)調(diào)用該函數(shù),具體使用方法
            是ProcAdd = (MYPROC) GetProcAddress(hinstLib, MakeIntResource(i)); (i代表
            函數(shù)在DLL中的序號(hào),可以用DUMPBIN工具查看),但是一般的都不用這種轉(zhuǎn)換序號(hào)的
            方式來(lái)取得函數(shù)的地址,因?yàn)檫@樣非常的不直觀!下面就用模塊定義文件(DEF)來(lái)
            避免DLL中函數(shù)的名字的改編問(wèn)題





            顯示加載DLL
            //MSDN上的對(duì)DLL進(jìn)程顯示加載的DEMO
            Using Run-Time Dynamic Linking
            You can use the same DLL in both load-time and run-time dynamic linking.
            The following example uses the LoadLibrary function to get a handle to
            the Myputs DLL (see Creating a Simple Dynamic-Link Library). If LoadLibr
            ary succeeds, the program uses the returned handle in the GetProcAddress
            function to get the address of the DLL's myPuts function. After calling
            the DLL function, the program calls the FreeLibrary function to unload
            the DLL.
            Because the program uses run-time dynamic linking, it is not necessary t
            o link the module with an import library for the DLL.
            This example illustrates an important difference between run-time and lo
            ad-time dynamic linking. If the DLL is not available, the application us
            ing load-time dynamic linking must simply terminate. The run-time dynami
            c linking example, however, can respond to the error.
            // A simple program that uses LoadLibrary and
            // GetProcAddress to access myPuts from Myputs.dll.

            #include <stdio.h>
            #include <windows.h>
            typedef int (*MYPROC)(LPTSTR);
            VOID main(VOID)
            {
               HINSTANCE hinstLib;
               MYPROC ProcAdd;
               BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
             // Get a handle to the DLL module.
            hinstLib = LoadLibrary(TEXT("DllTest"));
            // If the handle is valid, try to get the function address.
            if (hinstLib != NULL)
               {
                   ProcAdd = (MYPROC) GetProcAddress(hinstLib, TEXT("Proc"));
            // If the function address is valid, call the function.
             if (NULL != ProcAdd)
                   {
                       fRunTimeLinkSuccess = TRUE;
                       (ProcAdd) (TEXT("Message via DLL function\n"));
                   }
             // Free the DLL module.
            fFreeResult = FreeLibrary(hinstLib);
               }
             // If unable to call the DLL function, use an alternative.
            if (! fRunTimeLinkSuccess)
                  printf("Message via alternative method\n");
            }

            對(duì)以上的幾個(gè)函數(shù)作一些必要的說(shuō)明:
            LoadLibrary:加載指定的DLL,加載方式是先在當(dāng)前目錄中查找,如果找不到再再
            環(huán)境變量目錄下查找;
            還是看MSDN上的說(shuō)明
            The LoadLibrary function maps the specified executable module into the a
            ddress space of the calling process.
            HMODULE LoadLibrary(
             LPCTSTR lpFileName
            );
            Parameters
            lpFileName
            [in] Pointer to a null-terminated string that names the executable modul
            e (either a .dll or .exe file). The name specified is the file name of t
            he module and is not related to the name stored in the library module it
            self, as specified by the LIBRARY keyword in the module-definition (.def
            ) file.

            GetProcAddress:是取得已知的DLL中的函數(shù),返回指定函數(shù)的地址
            MSDN上的說(shuō)明
            This function returns the address of the specified exported DLL function
            .
            FARPROC GetProcAddress(
             HMODULE hModule,
             LPCWSTR lpProcName
            );
            Parameters
            hModule
            [in] Handle to the DLL module that contains the function.
            The LoadLibrary or GetModuleHandle function returns this handle.
            lpProcName
            [out] Pointer to a null-terminated string containing the function name,
            or specifies the function's ordinal value.
            If this parameter is an ordinal value, it must be in the low-order word;
            the high-order word must be zero.
            The lpProcName parameter must be in Unicode.
            Remark:
            The GetProcAddress function is used to retrieve addresses of exported fu
            nctions in DLLs.
            The spelling and case of the function name pointed to by lpProcName must
            be identical to that in the EXPORTS statement of the source DLL's modul
            e-definition (.def) file.
            The exported names of Win32 APIs might differ from the names you use whe
            n calling these functions in your code. This difference is hidden by mac
            ros used in the SDK header files.

            posts - 94, comments - 138, trackbacks - 0, articles - 94

            Copyright © RichardHe

            久久久久高潮毛片免费全部播放| 久久亚洲AV无码精品色午夜麻豆| 久久久久无码精品国产不卡| 伊人久久大香线蕉亚洲| 无码日韩人妻精品久久蜜桃| 久久国产高清字幕中文| 久久国产美女免费观看精品| 国产精品日韩欧美久久综合| 综合久久给合久久狠狠狠97色 | 国产精品久久久久久福利漫画| 精品无码久久久久久尤物| 国产福利电影一区二区三区,免费久久久久久久精 | 亚洲国产精品热久久| 伊人久久无码精品中文字幕| 无码超乳爆乳中文字幕久久 | 久久狠狠爱亚洲综合影院| 久久香综合精品久久伊人| 97久久精品午夜一区二区| 久久九九久精品国产| av色综合久久天堂av色综合在| 欧美综合天天夜夜久久| 久久久久亚洲av成人网人人软件| 69SEX久久精品国产麻豆| 婷婷久久综合九色综合绿巨人| 久久婷婷国产综合精品 | 伊人色综合久久天天| 亚洲色欲久久久综合网东京热| 久久精品人妻一区二区三区| 久久精品久久久久观看99水蜜桃| 日本久久久久久中文字幕| 久久99热只有频精品8| 亚洲精品tv久久久久久久久久| 亚洲综合婷婷久久| 2022年国产精品久久久久| 国内精品人妻无码久久久影院| 久久天天躁夜夜躁狠狠躁2022| 久久久久婷婷| 怡红院日本一道日本久久| 日本免费久久久久久久网站| 国产精品国色综合久久| 久久99国内精品自在现线|