在C++中調用DLL中的函數(3)

來源:互聯網
上載者:User

標籤:article   stat   簡單   msdn   解釋   get   std   interface   can   

轉自:http://www.cnblogs.com/woshitianma/p/3681745.html

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"的簡單解析(在C++中調用DLL中的函數(1))

 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檔案

 #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, "[email protected]@[email protected]@[email protected]")); //得到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檔案

 #include "stdafx.h"

 #ifdef __cplusplus         // if used by C++ code extern "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; }

在C++中調用DLL中的函數(3)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.