關於C和C++動態鏈接庫的幾個問題


問題:

     1.寫一段C++程序,編譯成動態鏈接庫后,C程序怎么訪問?

     2.寫一段C程序,編譯成動態鏈接庫后,C++程序怎么訪問?

     3.寫一個類,編譯成動態鏈接庫后,里面的public變量能否訪問? 

     對於以上問題,我在Visual C++ 6.0上進行了實驗。以下是實驗的過程和初步結論。 

     1. 建立動態鏈接庫是在新建工程中選擇Win32 Dynamic-Link Library,建立空的工程,在里面添加頭文件和實現文件(C程序用.c后綴,C++程序用.cpp后綴),以下給出C程序寫的動態鏈接庫代碼和C++寫的動態鏈接庫代碼(只包含一個Add函數):

Case 1:C程序的動態鏈接庫代碼: 

//CLib.h

#ifndef C_LIB_H

#define C_LIB_H

extern int __declspec(dllexport) add(int x,int y);

#endif

 

//CLib.c

#include "Clib.h"

int add( int x, int y )

{

return x + y;

}

 Case 2:C++程序的動態鏈接庫代碼:

//CplusplusLib.h

#ifndef LIB_H

#define LIB_H

extern "C" int __declspec(dllexport) add(int x, int y);

#endif

 

//CplusplusLib.cpp

#include "CplusplusLib.h"

int add( int x, int y )

{

return x + y;

}

      由此可發現,其代碼唯一不同的地方在於extern int __declspec(dllexport) add(int x,int y) 這一導出語句,在C程序中沒有”C” ,而在C++程序多了一個標識”C”。

      發生這個區別的原因是由於C++編譯器與C編譯器在對程序進行編譯的時候,對函數聲明的編譯會有區別,加上”C”,是為了告訴C++編譯器,使用C編譯器的方式對這一個函數聲明進行編譯。這樣,在C程序調用C++寫的動態鏈接庫的時候,才不會發生因為尋找不到對應的函數名(編譯后的)而發生Link錯誤。 

      再來看調用,調用方式有兩種:靜態和動態。這的確是印證了老師說的萬事皆有兩面的道理。

      使用動態的方式調用,不管在C還是在C++中,代碼都是一樣的,下面給出一個例子: 

typedef int(*lpAddFun)(int, int);

 

int main(int argc, char* argv[])

{

       HINSTANCE hDll;

       lpAddFun addFun;

       hDll = LoadLibrary("..//..//Debug//CplusplusDLL.dll");

       if (hDll != NULL)

       {

       addFun = (lpAddFun)GetProcAddress(hDll, "add");

       if (addFun != NULL)

       {

              int result = addFun(2, 3);

              printf("%d", result);

       }

       FreeLibrary(hDll);

       }

       return 0;

}

      思路是定義一個函數指針,在主程序中使用LoadLibrary這個庫函數載入DLL,然后再用GetProcAddress這個庫函數獲得DLL中函數所在的位置。調用完函數后使用FreeLibrary這個庫函數釋放DLL資源。 

      然而在靜態的方式中,C和C++的調用就略有不同,下面給出C++調用C庫的程序: 

// CplusplusApplicationStaticInvoke.cpp : Defines the entry point for the console application.

#include "stdafx.h"

#pragma comment(lib,"CDLL.lib")

extern "C" __declspec(dllimport) add(int x,int y);

int main(int argc, char* argv[])

{

       int result = add(2,3);

       printf("%d",result);

       return 0;

}

       #pragma comment(lib,"CDLL.lib")這一句是表示在鏈接的時候將CDLL.dll這個動態鏈接庫鏈接到這個程序當中。 

       而在C調用C++庫的例子中:

// CApplicationStaticInvoke.cpp : Defines the entry point for the console application.

#include "stdafx.h"

#pragma comment(lib,"CplusplusDLL.lib")

extern __declspec(dllimport) add(int x,int y);

int main(int argc, char* argv[])

{

       int result = add(2,3);

       printf("%d",result);

       return 0;

}

       明顯看到extern __declspec(dllimport) add(int x,int y); 與上一例子不同,少了一個”C” 

      這個原因很明顯,就是為了動態庫中的函數名進行對應。在CDLL中函數當然是使用C編譯器的方式進行編譯的,所以在調用程序中,在聲明外部函數的時候,必須加上”C”,以使的這個C++程序,在編譯的時候使用C編譯的方法對這個外部函數聲明進行編譯,否則在編譯運行的時候就會報鏈接錯誤。在C++調用C的例子中我們將”C”去掉,結果:

LNK2001: unresolved external symbol "__declspec(dllimport) int __cdecl add(int,int)" (__imp_?add@@YAHHH@Z)

上面add@@YAHHH@Z就是add函數用C++的編譯方法編譯后的得到的函數名,而C編譯得到的應該是_add,鏈接的時候匹配不成功,因此就報錯了。

 

       2. 關於類的public變量能夠被訪問,能否被直接訪問,這里我仍沒有找出答案,等待老師解答,但是有一個間接的方法能訪問到類里面的變量。就是使用類的全局變量,將其導出即可,例子如下: 

#ifdef CLASSDLL_EXPORTS

#define CLASSDLL_API __declspec(dllexport)

#else

#define CLASSDLL_API __declspec(dllimport)

#endif

 

class CLASSDLL_API CClassDLL {

public:

       CClassDLL(void);

       //Added public member

       int testNumber;

       // TODO: add your methods here.

};

 

extern CLASSDLL_API int nClassDLL;

CLASSDLL_API int fnClassDLL(void);

      上面的標准宏是為了對導入導出進行匹配,這里先略過。

      注意extern CLASSDLL_API int nClassDLL; 這句代碼定義了這個DLL中的一個全局變量。注意這個時候宏CLASSDLL_API應該是__declspec(dllexport)表示導出該變量。 

     在調用的時候:

#pragma comment(lib,"ClassDLL.lib")

extern int _declspec(dllimport) nClassDLL;

int main()

{

…

}

       這樣就可以直接在程序中訪問nClassDLL了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM