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

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

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




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

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

            以上操作只有在VC++創建的的可執行模塊來調用該DLL,如果使用其他的編譯器的模
            塊來調用該DLL,就需要執行一些額外的操作。
            C編譯在進行編譯的時候也會進行名字的改編,當函數使用_stdcall(WINAPI)調用
            規則時,MS編譯器就會改編函數的名稱。
            比如:__declspec(DLLexport)  LONG __stdcall  Proc(int a ,int b);
            編譯器會改編為__Proc@8
            因此 當非C++或非C編譯器調用該DLL中的函數Proc時,就會出現找不到函數的情況

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

                            




            下面是def的具體使用方法

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

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


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

            有了上面的那些文件之后就可以在如何地方調用這些函數了

            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的時候函數會發生名字改編;主要在非C++環境中就不能識別
            該函數了,所以這里應該定義模塊文件類型DEF,主要就方便了非C++程序可以調用
            該DLL里面的函數
            再使用顯示加載前必須要注意的是名字的改編問題,因為再動態加載中名字改編后
            在加載就得不原來的函數名字了,這樣加載就會失敗。但是可以用另外一種方法加
            載:MSDN上對GetProAddress中的第二個參數是這樣說明的Pointer to a null-ter
            minated string that specifies the function or variable name, or the func
            tion's ordinal value.也就是說可以使用函數的序號來調用該函數,具體使用方法
            是ProcAdd = (MYPROC) GetProcAddress(hinstLib, MakeIntResource(i)); (i代表
            函數在DLL中的序號,可以用DUMPBIN工具查看),但是一般的都不用這種轉換序號的
            方式來取得函數的地址,因為這樣非常的不直觀!下面就用模塊定義文件(DEF)來
            避免DLL中函數的名字的改編問題





            顯示加載DLL
            //MSDN上的對DLL進程顯示加載的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");
            }

            對以上的幾個函數作一些必要的說明:
            LoadLibrary:加載指定的DLL,加載方式是先在當前目錄中查找,如果找不到再再
            環境變量目錄下查找;
            還是看MSDN上的說明
            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中的函數,返回指定函數的地址
            MSDN上的說明
            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

            亚洲精品WWW久久久久久| 亚洲成人精品久久| 久久这里都是精品| 色综合久久中文字幕无码| 韩国无遮挡三级久久| 一本色道久久综合狠狠躁篇 | 日本久久久久久久久久| 思思久久好好热精品国产| 久久狠狠高潮亚洲精品| 欧美无乱码久久久免费午夜一区二区三区中文字幕 | 久久人人爽人人爽人人片AV不| 精品久久久久久无码中文字幕一区| 亚洲国产精久久久久久久| 久久久久人妻一区二区三区| 久久久精品一区二区三区| 怡红院日本一道日本久久 | 国内精品久久久久影院老司| 2021久久精品国产99国产精品| 久久午夜免费视频| 国产2021久久精品| 精品蜜臀久久久久99网站| 波多野结衣久久一区二区| 国产精自产拍久久久久久蜜| 久久人人爽人人爽人人片av高请| 伊人久久国产免费观看视频 | 久久亚洲精品无码aⅴ大香| 日本免费久久久久久久网站| 久久久久久夜精品精品免费啦| 亚洲人成网站999久久久综合| 久久久久黑人强伦姧人妻| 国产精品免费久久久久久久久 | 伊人久久精品无码av一区| 亚洲人成无码久久电影网站| 久久久久久久亚洲精品| 亚洲国产精品久久久久婷婷老年| 精品久久久久久综合日本| 久久久免费精品re6| 国产精品久久久久9999高清| 国产精品久久久久久| 欧美久久综合性欧美| 久久精品国产一区二区三区|