C++在VS下建立、調用dll

來源:互聯網
上載者:User

1.dll的優點

代碼複用是提高軟體開發效率的重要途徑。一般而言,只要某部分代碼具有通用性,就可將它構造成相對獨立的功能模組並在之後的項目中重複使用。比較常見的例子是各種應用程式架構,ATL、MFC等,它們都以原始碼的形式發布。由於這種複用是“源碼層級”的,原始碼完全暴露給了程式員,因而稱之為“白盒複用”。“白盒複用”的缺點比較多,總結起來有4點。
暴露了原始碼;多份拷貝,造成儲存浪費;
容易與程式員的“普通”代碼發生命名衝突;
更新功能模組比較困難,不利於問題的模組化實現;
實際上,以上4點概括起來就是“暴露的原始碼”造成“代碼嚴重耦合”。為了彌補這些不足,就提出了“二進位層級”的代碼複用。使用二進位層級的代碼複用一定程度上隱藏了原始碼,對於緩解代碼耦合現象起到了一定的作用。這樣的複用被稱為“黑盒複用”。
說明:實現“黑盒複用”的途徑不只dll一種,靜態連結庫甚至更進階的COM組件都是。

2.dll的建立

參考程式原文:http://msdn.microsoft.com/zh-cn/library/ms235636.aspx
建立“Win32項目”,選擇應用程式類型為"DLL”,其他預設。添加標頭檔testdll.h

//testdll.h#ifdef TESTDLL_EXPORTS  #define TESTDLL_API __declspec(dllexport)   #else  #define TESTDLL_API __declspec(dllimport)   #endif  namespace MathFuncs  {      // This class is exported from the testdll.dll      class MyMathFuncs      {      public:           // Returns a + b          static TESTDLL_API double Add(double a, double b);        // Returns a - b          static TESTDLL_API double Subtract(double a, double b);        // Returns a * b          static TESTDLL_API double Multiply(double a, double b);        // Returns a / b          // Throws const std::invalid_argument& if b is 0          static TESTDLL_API double Divide(double a, double b);    };  }

當定義了符號TESTDLL_EXPORTS,TESTDLL_API被設定為 __declspec(dllexport) 修飾符,This modifier enables the function to be exported by the DLL so that it can be used by other applications。若未定義則TESTDLL_API被設定為__declspec(dllimport),This modifier enables the compiler to optimize the importing of the function from the DLL for use in other applications。當DLL項目產生時,TESTDLL_EXPORTS預設是定義的,所以預設設定的是__declspec(dllexport) 修飾符。
添加cpp檔案

// testdll.cpp : 定義 DLL 應用程式的匯出函數。#include "stdafx.h"#include "testdll.h"  #include <stdexcept>  using namespace std;  namespace MathFuncs  {      double MyMathFuncs::Add(double a, double b)      {          return a + b;      }      double MyMathFuncs::Subtract(double a, double b)      {          return a - b;      }      double MyMathFuncs::Multiply(double a, double b)      {          return a * b;      }      double MyMathFuncs::Divide(double a, double b)      {          if (b == 0)          {              throw invalid_argument("b cannot be zero!");          }          return a / b;      }  }

編譯就會產生對應的dll檔案,同時也會產生對應的lib檔案。
注意:a.DLL中匯出函數的聲明有兩種方式:在函式宣告中加上__declspec(dllexport);採用模組定義(.def)檔案聲明。詳見:http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792099.html
b.對於C檔案建立dll時或者想使用C編譯器建立dll時,建議使用 extern “C” 標誌,參見extern "C"的簡單解析

3.dll的調用

應用程式使用DLL可以採用兩種方式:一種是隱式連結(調用),另一種是顯式連結。在使用DLL之前首先要知道DLL中函數的結構資訊。VS在VC\bin目錄下提供了一個名為Dumpbin.exe的小程式,用它可以查看DLL檔案中的函數結構。兩種的對比詳見:http://blog.sina.com.cn/s/blog_53004b4901009h3b.html
隱式連結採用靜態載入的方式,比較簡單,需要.h、.lib、.dll三件套。建立“控制台應用程式”或“空項目”。配置如下:
項目->屬性->配置屬性->VC++ 目錄-> 在“包含目錄”裡添加標頭檔testdll.h所在的目錄
項目->屬性->配置屬性->VC++ 目錄-> 在“庫目錄”裡添加標頭檔testdll.lib所在的目錄
項目->屬性->配置屬性->連結器->輸入-> 在“附加依賴項”裡添加“testdll.lib”(若有多個 lib 則以空格隔開)
添加cpp檔案

//mydll.cpp#include <iostream>  #include "testdll.h"  using namespace std;  int main()  {      double a = 7.4;      int b = 99;      cout << "a + b = " <<          MathFuncs::MyMathFuncs::Add(a, b) << endl;      cout << "a - b = " <<          MathFuncs::MyMathFuncs::Subtract(a, b) << endl;      cout << "a * b = " <<          MathFuncs::MyMathFuncs::Multiply(a, b) << endl;      cout << "a / b = " <<          MathFuncs::MyMathFuncs::Divide(a, b) << endl;      try      {          cout << "a / 0 = " <<              MathFuncs::MyMathFuncs::Divide(a, 0) << endl;       }      catch (const invalid_argument &e)       {          cout << "Caught exception: " << e.what() << endl;       }      return 0;  }

現在可以編譯通過了,但是程式運行就報錯,還需要將testdll.dll複製到當前項目產生的可執行檔所在的目錄。
顯式連結是應用程式在執行過程中隨時可以載入DLL檔案,也可以隨時卸載DLL檔案,這是隱式連結所無法作到的,所以顯式連結具有更好的靈活性,對於解釋性語言更為合適。
建立項目,不需要特殊配置,添加cpp檔案

/* *作者:侯凱 *說明:顯式調用DLL *日期:2013-6-5*/#include<Windows.h> //載入的標頭檔#include<iostream>using namespace std;int main()  {      typedef double (*pAdd)(double a, double b);    typedef double (*pSubtract)(double a, double b);     HMODULE hDLL = LoadLibrary("testdll.dll"); //載入dll檔案     if(hDLL != NULL)      {          pAdd fp1 = pAdd(GetProcAddress(hDLL, MAKEINTRESOURCE(1))); //得到dll中的第一個函數        if(fp1 != NULL)          {               cout<<fp1(2.5, 5.5)<<endl;         }          else          {              cout<<"Cannot Find Function "<<"add"<<endl;          }          pSubtract fp2 = pSubtract(GetProcAddress(hDLL, "?Subtract@MyMathFuncs@MathFuncs@@SANNN@Z")); //得到dll中標示為"?..."的函數,C++編譯器考慮了函數的參數        if(fp2 != NULL)          {              cout<<fp2(5.5, 2.5)<<endl;          }          else          {              cout<<"Cannot Find Function "<<"Subtract"<<endl;          }          FreeLibrary(hDLL);      }      else      {          std::cout<<"Cannot Find "<<"testdll"<<std::endl;      }      return 1;  }

顯式調用的問題:在DLL檔案中,dll工程中函數名稱在編譯產生DLL的過程中發生了變化(C++編譯器),在DLL檔案中稱變化後的字元為“name標示”。GetProcAddress中第二個參數可以由DLL檔案中函數的順序獲得,或者直接使用DLL檔案中的”name標示”,這個標示可以通過Dumpbin.exe小程式查看。如果C++編譯器下,想讓函數名更規範(和原來工程中一樣),具體方法詳見:http://blog.csdn.net/btwsmile/article/details/6676802。
當然,為了讓函數名更規範,最常用的方式是:建立dll過程中使用C編譯器來編譯函數,這樣DLL檔案中的函數名和原dll工程中的函數名就一致了。

4.更一般的顯式調用

為瞭解決上部分最後的問題,可以使用 extern “C” 為dll工程中的函數建立C串連,簡單的樣本工程如下。
在DLL建立的工程中,添加cpp檔案

/* *作者:侯凱 *說明:建立dll,使用C介面——C編譯器產生的dll中函數的"name標示"仍為addfun *日期:2013-6-5*/// cdll.cpp : 定義 DLL 應用程式的匯出函數。//#include "stdafx.h"#ifdef __cplusplus         // if used by C++ codeextern "C" {                  // we need to export the C interface#endif__declspec(dllexport) int addfun(int a, int b){        return a+b;}#ifdef __cplusplus}#endif

編譯即可產生DLL檔案。在dll調用工程中,添加cpp檔案

/* *作者:侯凱 *說明:顯式調用dll *日期:2013-6-5*/#include <windows.h>#include <iostream>using namespace std;void main(){    typedef int(*FUNA)(int,int);    HMODULE hMod = LoadLibrary("cdll.dll");//dll路徑    if (hMod)    {        FUNA addfun = (FUNA)GetProcAddress(hMod, TEXT("addfun"));//直接使用原工程函數名         if (addfun != NULL)        {            cout<<addfun(5, 4)<<endl;        }        else        {            cout<<"ERROR on GetProcAddress"<<endl;        }        FreeLibrary(hMod);    }    else        cout<<"ERROR on LoadLibrary"<<endl;}

運行,這樣便可以調用dll的函數了。再進一步,上述dll檔案如果通過隱式調用,利用.dll、.lib檔案,調用函數應為

//隱式連結#include <iostream>#pragma comment(lib,"cdll.lib")using namespace std;extern "C" _declspec(dllimport) int addfun(int a,int b);//載入addfun函數,這裡起到了.h檔案的作用//dll中使用C編譯器 故這裡需要extern "C" 如果dll中無extern "C"//此處為:_declspec(dllimport) int addfun(int a,int b);void main(){    cout<<addfun(5,4)<<endl;}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.