1. 程式人生 > >動態庫(非MFC動態庫)

動態庫(非MFC動態庫)

name 單獨 打開 源代碼 保留 c++編譯器 light width endif

一、C語言動態庫

  1、創建C語言動態庫,並封裝函數:

    1)創建新工程:Win32 Dynamic-Link Library

      技術分享

    2)添加SourceFiles文件:Cdll.c

      技術分享

      Cdll.c中的內容:      

//創建C的動態庫

//_declspec(dllexport)聲明導出
_declspec(dllexport)int Cdll_add(int add1,int add2){
    return add1+add2;
}

_declspec(dllexport)int Cdll_sub(int sub1,int sub2){
    return sub1-sub2;
}

    3)編譯、鏈接

      註意:調用動態庫中的函數時,須執行函數導出,庫函數的導出有兩種方法:

      ①方法一:用_declspec(dllexport)聲明導出

      ②方法二:模塊定義文件.def

        如:LIBRARY 庫文件名(無後綴)

          EXPORTS

            函數名1 @1

            函數名2 @2

      Build後在當前工程的Debug文件夾下生成Cdll.dll和Cdll.lib文件

      這裏生成的Cdll.dll文件中保存了函數的實際偏移地址和對應編號,Cdll.lib文件中並非函數的源代碼而是存放的動態庫dll中的函數偏移地址編號

      將Cdll.dll文件置於工作區下的bin文件夾中,將Cdll.lib文件置於工作區下的lib文件夾中

  2、調用C語言動態庫中的函數:

    ①靜態調用(隱式鏈接)

      註意:靜態調用(隱式鏈接)調用動態庫中的函數時,在聲明函數時須在函數原型前加_declspec(dllimport)聲明導入

      但我們這裏是用C程序來調用C語言動態庫中的函數,無須函數聲明

      1)創建工作工程:Win32 Console Application

        技術分享

      2)添加SourceFiles文件:UseCdll.c

        技術分享

        UseCdll.c中的內容:        

//靜態調用C的動態庫

//告訴鏈接器去哪兒抓偏移地址編號
#pragma comment(lib,"../lib/Cdll.lib")

int main(){
    int sum,sub;
    sum=Cdll_add(5,3);
    sub=Cdll_sub(5,3);
    printf("sum=%d,sub=%d\n",sum,sub);
    return 0;
}

      3)編譯、鏈接

        註意:調用動態庫的情況下,須將生成的dll文件(即Cdll.dll)與執行文件(即UseCdll.exe)放在同一目錄下,程序才可運行

        這裏因為之前已將Cdll.dll文件統一置於工作區下的bin文件夾中,因此在本次調用時須修改VC6的菜單欄->工程->設置->連接->輸出文件名:../bin/UseCdll.exe

    ②動態調用(顯式鏈接)

二、C++動態庫

  1、創建C++動態庫,並封裝函數:

    註意:調用動態庫中的函數時需要執行函數導出,庫函數的導出有兩種方法:

      ①方法一:用_declspec(dllexport)聲明導出

      ②方法二:模塊定義文件.def

        如:

          LIBRARY 庫文件名(無後綴)

EXPORTS

            函數名1 @1

            函數名2 @2

        註:.def 文件中的註釋格式為“;註釋”,且為單行註釋。

    ①“_declspec(dllexport)聲明導出”方式創建C++動態庫

      1)創建新工程:Win32 Dynamic-Link Library

        技術分享

      2)添加SourceFiles文件:CPPdll.cpp

        技術分享

        CPPdll.cpp中的內容:       

//用"聲明導出_declspec(dllexport)"方式創建C++動態庫

//函數導出
_declspec(dllexport)int CPPdll_add(int add1,int add2){
    return add1+add2;
}

_declspec(dllexport)int CPPdll_sub(int sub1,int sub2){
    return sub1-sub2;
}

      3)編譯、鏈接

        Build後,在當前工程的Debug文件夾下生成CPPdll.dll和CPPdll.lib文件

        CPPdll.dll文件保存了函數的實際偏移地址和對應編號,CPPdll.lib文件並非函數源代碼而是存放的動態庫dll中的函數名和偏移地址編號

        將CPPdll.dll文件置於工作區下的bin文件夾中,將CPPdll.lib文件置於工作區下的lib文件夾中

    ②“模塊定義文件.def”方式創建C++動態庫

      1)創建新工程:Win32 Dynamic-Link Library

        技術分享

      2)添加SourceFiles文件:CPPdll2.def

        技術分享

        CPPdll2.def中的內容:        

LIBRARY CPPdll2
EXPORTS
    CPPdll_add @1
    CPPdll_sub @2

      3)添加SourceFiles文件:CPPdll2.cpp

        技術分享

        CPPdll2.cpp中的內容:        

//用"模塊定義文件導出.def"方式創建C++動態庫

//函數導出
int CPPdll_add(int add1,int add2){
    return add1+add2;
}

int CPPdll_sub(int sub1,int sub2){
    return sub1-sub2;
}

      4)編譯、鏈接

        Build後,在當前工程的Debug文件夾下生成CPPdll2.dll和CPPdll2.lib文件

        CPPdll2.dll文件保存了函數的實際偏移地址和對應編號,CPPdll2.lib文件並非函數源代碼而是存放的動態庫dll中的函數名和偏移地址編號

        將CPPdll2.dll文件置於工作區下的bin文件夾中,將CPPdll2.lib文件置於工作區下的lib文件夾中

  2、調用C++動態庫中的函數:

    ①靜態調用(隱式鏈接)

      1)創建工作工程:Win32 Console Application

        技術分享

      2)添加SourceFiles文件:UseCPPdll.cpp

        技術分享

        UseCPPdll.cpp中的內容:        

//用"隱式鏈接"方式調用C++動態庫
//對應的在創建C++動態庫時使用的是"聲明導出_declspec(dllexport)"方式

#include <stdio.h>
int CPPdll_add(int add1,int add2);
int CPPdll_sub(int sub1,int sub2);

//告訴編譯器到哪去抓函數的導出偏移地址編號
#pragma comment(lib,"../lib/CPPdll.lib")

/*****************************************/
//C++編譯器調用C語言動態庫中的函數
extern "C"int Cdll_add(int add1,int add2);
extern "C"int Cdll_sub(int sub1,int sub2);
#pragma comment(lib,"../lib/Cdll.lib")
/*****************************************/

int main(){
    int sum=CPPdll_add(5,6);
    int sub=CPPdll_sub(5,6);
    printf("sum=%d,sub=%d\n",sum,sub);

    /*********************************/
    sum=Cdll_add(5,8);
    sub=Cdll_sub(5,8);
    printf("sum=%d,sub=%d\n",sum,sub);
    /********************************/

    return 0;
}

      3)編譯、鏈接

        註意:調用動態庫的情況下,須將生成的dll文件(即CPPdll.dll)與執行文件(即UseCPPdll.exe)放在同一目錄下,程序才可運行

        這裏因為之前已將CPPdll.dll文件統一置於工作區下的bin文件夾中,因此在本次調試時,須修改VC6的菜單欄->工程->設置->連接->輸出文件名:../bin/UseCPPdll.exe

      4)C++編譯器在調用動態庫中的函數時,須進行函數聲明:

        #include <stdio.h>

        int CPPdll_add(int add1,int add2);

        int CPPdll_sub(int sub1,int sub2);

      5)C++編譯器在調用C語言動態庫中的函數時,會對函數名進行換名,須使用extern “C”來抑制C++編譯器的換名

        extern "C"int Cdll_add(int add1,int add2);

        extern "C"int Cdll_sub(int sub1,int sub2);

        #pragma comment(lib,"../lib/Cdll.lib")

      6)這裏C++編譯器在調用動態庫中的函數時,未見將函數導入,是因為將函數聲明放在了調用程序內部,如果單獨放在頭文件中,則須將函數導入:

        //UseCPPdll.h

        _declspec(dllimport)int CPPdll_add(int add1,int add2);

        _declspec(dllimport)int CPPdll_sub(int sub1,int sub2);

        若調用的是C語言動態庫(.c生成的.dll、.lib)中的函數,還須在int前加extern "C"

    ②動態調用(顯式鏈接)

      註意:C++程序在動態調用(顯式鏈接)C++動態庫中的函數時,會對函數進行換名,故推薦用“模塊定義文件.def”的方式導出函數

      1)創建工作工程:Win32 Console Application

        技術分享

      2)添加SourceFiles文件:UseCPPdll2.cpp

        技術分享

        UseCPPdll2.cpp中的內容:        

//用"顯示連接"方式調用C++動態庫
//用此方法調用時,C++編譯器會對庫中函數的調用進行換名
//因此在創建C++動態庫時,推薦使用"模塊定義導出.def"方式將函數導出

#include <windows.h>
#include <stdio.h>

typedef int(*DLL_ADD)(int m,int n);
typedef int(*DLL_SUB)(int m,int n);

int main(){
    //找到dll文件並使文件中的內容進入內存
    HINSTANCE hDll=LoadLibrary("CPPdll2.dll");
    printf("hDll:%d\n",hDll);

    //獲取函數的絕對地址並進行函數調用
    
    DLL_ADD myAdd=(DLL_ADD)GetProcAddress(hDll,"CPPdll_add");
    printf("myAdd:%p\n",myAdd);
    int sum=myAdd(5,5);
    printf("sum=%d\n",sum);

    DLL_SUB mySub=(DLL_SUB)GetProcAddress(hDll,"CPPdll_sub");
    printf("mySub:%p\n",mySub);
    int sub=mySub(5,5);
    printf("sub=%d\n",sub);

    FreeLibrary(hDll);
    return 0;
}

      3)編譯、鏈接

        註意:調用動態庫的情況下,須將生成的dll文件(即CPPdll2.dll)與執行文件(即UseCPPdll2.exe)放在同一目錄下,程序才可運行

        這裏因為之前已將CPPdll2.dll文件統一置於工作區下的bin文件夾中,因此在本次調試時,須修改VC6的菜單欄->工程->設置->連接->輸出文件名:../bin/UseCPPdll2.exe

        動態加載:

          1.定義函數指針類型:typedef ...

          2.加載動態庫

            HMODULE LoadLibrary (

              LPCTSTR lpFileName // 動態庫文件名(按路徑規則搜索)或者用絕對/相對路徑(按指定路徑加載)

            );

            成功返回動態庫實例句柄(HINSTANCE),失敗返回NULL。

          3.獲取函數地址

            FARPROC GetProcAddress (

              HMODULE hModule, // 動態庫實例句柄

              LPCSTR lpProcName, // 函數名(註意C++換名問題)

            );

            成功返回函數地址,失敗返回NULL。

          4.卸載動態庫

            BOOL FreeLibrary (

              HMODULE hModule // 動態庫實例句柄

            );

            成功返回TRUE,失敗返回FALSE。

          5.可執行程序調用LoadLibrary時加載動態庫,調用FreeLibrary時卸載動態庫

  

  3、在C++動態庫中封裝類

    註意:動態庫中類的導出只能用“_declspec(dllexport)”方式,不能使用“模塊定義文件.def”方式

    1)創建新工程:Win32 Dynamic-Link Library

      技術分享

    2)添加HeaderFiles文件:dllClass.h

      技術分享

      dllClass.h中的內容:      

#ifndef DLLCLASS_H
#define DLLCLASS_H

//宏開關
//定義關於庫中類的“導出”(我們編寫庫時為導出)或“導入”(用戶使用庫時需導入)的宏定義
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport)
#else
#define EXT_CLASS _declspec(dllimport)
#endif

class EXT_CLASS CMath{
public:
    int Add(int add1,int add2);
    int Sub(int sub1,int sub2);
};

#endif

      類導出的宏開關:

        #ifdef DLLCLASS_EXPORTS

        #define EXT_CLASS _declspec(dllexport)

        #else

        #define EXT_CLASS _declspec(dllimport)

        #endif

        class EXT_CLASS CMath{

          ......

        }

    3)添加SourceFiles文件:dllClass.cpp

      技術分享

      dllClass.cpp中的內容:      

//在庫中封裝類
//在.cpp源文件中實現函數的功能,編譯連接後生成的.dll文件用戶才不可見具體的功能實現過程

//必須在頭文件前打開宏開關,設為導出:_declspec(dllexport)
#define DLLCLASS_EXPORTS
#include "dllClass.h"
#include <windows.h>
#include <stdio.h>

//入口函數
BOOL WINAPI DllMain(HINSTANCE hDll,DWORD fdwReason,LPVOID pParam){
    switch(fdwReason){
        case DLL_PROCESS_ATTACH:  //動態庫被別的進程加載
            //申請資源、初始化工作
            printf("Loading...\n");
            break;
        case DLL_PROCESS_DETACH:  //動態庫被別的進程卸載
            //善後處理
            printf("UnLoading...\n");
            break;
    }
    return TRUE;
}

int CMath::Add(int add1,int add2){
    return add1+add2;
}

int CMath::Sub(int sub1,int sub2){
    return sub1-sub2;
}

    4)編譯、鏈接

      Build後,在當前工程的Debug文件夾下生成dllClass.dll和dllClass.lib文件

      dllClass.dll文件保存了函數的實際偏移地址和對應編號,dllClass.lib文件並非函數源代碼而是存放的動態庫dll中的函數名和偏移地址編號

      將dllClass.dll文件置於工作區下的bin文件夾中,將dllClass.lib文件置於工作區下的lib文件夾中

  4、調用C++動態庫中的類:

    1)創建工作工程:Win32 Console Application

      技術分享

    2)添加SourceFiles文件:UsedllClass.cpp

      技術分享

      UsedllClass.cpp中的內容:     

//調用dllClass.dll庫

#include <stdio.h>
#include "../dllClass/dllClass.h"

#pragma comment(lib,"../lib/dllClass.lib")

int main(){
    CMath math;
    int sum=math.Add(5,2);
    int sub=math.Sub(5,2);
    printf("sum=%d,sub=%d\n",sum,sub);
    return 0;
}

    3)編譯、鏈接

      在UsedllClass.cpp未見有對宏開關的定義語句“#define DLLCLASS_EXPORTS”,故宏開關自動設為導入:_declspec(dllimport)

      註意:調用動態庫的情況下,須將生成的dll文件(即dllClass.dll)與執行文件(即UsedllClass.exe)放在同一目錄下,程序才可運行

      這裏因為之前已將dllClass.dll文件統一置於工作區下的bin文件夾中,因此在本次調試時,須修改VC6的菜單欄->工程->設置->連接->輸出文件名:../bin/UsedllClass.exe

三、動態庫的入口函數:

  入口函數不是動態庫所必須的,常用於動態庫內部初始化或善後處理

  BOOL WINAPI DllMain (

    HANDLE hDLL, // 動態庫實例句柄

    DWORD fdwReason, // 被調用的原因

    LPVOID lpvReserved // 保留值

  );

  返回TRUE表示動態庫加載成功,FALSE表示失敗。

    fdwReason取值:

      DLL_PROCESS_ATTACH - 進程加載,在主線程中調用LoadLibrary

      DLL_PROCESS_DETACH - 進程卸載,在主線程中調用FreeLibrary

      DLL_THREAD_ATTACH - 線程加載,在子線程中調用LoadLibrary

      DLL_THREAD_DETACH - 線程卸載,在子線程中調用FreeLibrary

動態庫(非MFC動態庫)