動態鏈接庫的製作

動態鏈接庫
自從微軟推出第一個版本的Windows操作系統以來,動態鏈接庫(DLL)一直是Windows操作系統的基礎。

動態鏈接庫通常都不能直接運行,也不能接收消息。
它們是一些獨立的文件,其中包含能被可執行程序或其它DLL調用來完成某項工作的函數。
只有在其它模塊調用動態鏈接庫中的函數時,它才發揮作用。
Windows API中的所有函數都包含在DLL中。
其中有3個最重要的DLL,Kernel32.dll,它包含用於管理內存、進程和線程的各個函數;
User32.dll,它包含用於執行用戶界面任務(如窗口的創建和消息的傳送)的各個函數;
GDI32.dll,它包含用於畫圖和顯示文本的各個函數。


靜態庫:
函數和數據被編譯進一個二進制文件(通常擴展名爲.LIB)。
在使用靜態庫的情況下,在編譯鏈接可執行文件時,鏈接器從庫中複製這些函數和數據並把它們和應用程序的其它模塊組合起來創建最終的可執行文件(.EXE文件)。

在使用動態庫的時候,往往提供兩個文件:一個引入庫和一個DLL。
引入庫包含被DLL導出的函數和變量的符號名,DLL包含實際的函數和數據。
在編譯鏈接可執行文件時,只需要鏈接引入庫,DLL中的函數代碼和數據並不複製到可執行文件中,在運行的時候,再去加載DLL,訪問DLL中導出的函數。


使用動態鏈接庫的好處是什麼?

可以採用多種編程語言來編寫。
增強產品的功能。
提供二次開發的平臺。
簡化項目管理。
可以節省磁盤空間和內存。
有助於資源的共享。
有助於實現應用程序的本地化。


動態鏈接庫的加載模式
隱式鏈接
顯示加載

新建工程項 選擇 WIN32 Dynamic-Link Library-》
新建一個C++文件-》
輸入
_declspec(dllexport) int   add(int a,int b)-->需要加_declspec(dllexport) 這個標識符
{
    return a+b;
}

_declspec(dllexport) int  subtract(int a,int b)
{
    return a-b;
}

也可以寫成
#define DLLx_API extern "C" _declspec(dllexport)
#include "Dllx.h"

#include <Windows.h>
#include <stdio.h>

int _stdcall add(int a,int b)
{
    return a+b;
}

int _stdcall subtract(int a,int b)
{
    return a-b;
}
-》編譯完成,打開命令提示符 輸入 D:DLLX/Debug>dumpbin--> D:DLLX/Debug爲生成的DLL的地址
如果找不到dumpbin這個命令,就打開VC安裝目錄,找到目錄下的VC98/Bin/下有DUMPBIN.EXE
在使用這個EXE之前,將安裝目錄下的VCVARS32.BAT拖拽到命令提示符下 D:DLLX/Debug>後面

D:DLLX/Debug>dumpbin -exports DLLX.dll 查看該DLL導出的函數

編譯好後,新建一個基於DIALOG的app wizard項目,
將新生成的DLLX.lib和DLLX.dll拷貝到這個項目的目錄裏
在Project Settings -》Link-》Object/library modules裏輸入DLLX.lib

添加
extern int add(int a,int b);
extern int subtract(int a,int b);
也可以
_declspec(dllimport) int add(int a,int b);--》告訴編譯器是從DLL裏獲得的,編譯成更高效率的代碼
_declspec(dllimport) int subtract(int a,int b);

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


但是這樣做,調用你DLL的人不找到裏面的方法?
則在DLL項目裏添加一個CPP文件,將
_declspec(dllimport) int add(int a,int b);
_declspec(dllimport) int subtract(int a,int b);

也可以寫成
#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllimport)---》"C"這個是需要注意的,有個缺點不能到處一個類的成員函數
#endif

DLLx_API int _stdcall add(int a,int b);---》_stdcall爲標準約定
DLLx_API int _stdcall subtract(int a,int b);


寫入進去,並且讓調用的項目裏調用此文件
#include "../Dllx/Dllx.h"
這樣別人看到這個頭文件就知道該調用哪些方法


HWND GetForegroundWindow(VOID); 獲得當前正在運行的 窗口句柄


怎麼定義模塊定義文件?
新建一個動態鏈接庫項目,在目錄下新建一個記事本,並重定義爲DDLX.def
並Project-》add to project-》DDLX.def,添加到項目裏
在DDLX.def裏輸入
LIBRARY DDLX--》LIBRARY 用於指定動態鏈接庫的內部名稱 名稱必須和生成動態鏈接庫的名稱要匹配

EXPORTS
add
subtract

而此時的CPP文件裏只需要寫入
int _stdcall add(int a,int b)
{
    return a+b;
}

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

此時,只需要將DDLX.dll文件拷貝進入引入的項目目錄就可以了
HMODULE LoadLibrary(
  LPCTSTR lpFileName   // file name of module
);--》映射一個可執行模塊或者可執行文件到一個進程地址空間


FARPROC GetProcAddress(
  HMODULE hModule,    // handle to DLL module
  LPCSTR lpProcName   // function name---》函數名稱
);---》獲取動態鏈接庫導出函數或者變量的地址

引用的代碼如下:
    HINSTANCE hInst;
    hInst=LoadLibrary("Dllx.dll");
    typedef int (*ADDPROC)(int a,int b);
     ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"add");
         //ADDPROC Add=(_stdcall ADDPROC)GetProcAddress(hInst,"add"); -->加入上面定義的函數是標準約定,這裏就必須用標準約定來讀取
     
       if(!Add)
    {
        MessageBox("獲取函數地址失敗!");
        return;
    }
    CString str;
    str.Format("5+3=%d",Add(5,3));
    MessageBox(str);
        FreeLibrary(hInst);


那麼動態添加動態鏈接庫有哪些好處?
當非動態添加動態鏈接庫的時候,在進程啓動的時候,需要將動態鏈接庫中的函數全部添加到進程
並拷貝到內存中,無形中給程序和內存帶了負擔,可能一些函數都是不需要的,而動態添加,
只在需要用的時候,再加入,,起到優化項目運行效率上有很好的效果


BOOL WINAPI DllMain(--》DllMain 是動態鏈接庫的入口點,提供不提供都可以
  HINSTANCE hinstDLL,  // handle to the DLL module
  DWORD fdwReason,     // reason for calling function
  LPVOID lpvReserved   // reserved
);


創建一個MFC AppWizard[dll]項目

BOOL FreeLibrary(--》減少一個加載的動態鏈接庫引用計數,而且hModule   將不再有效
  HMODULE hModule   // handle to DLL module
);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章