1、例子
以下內容來自Creating and consuming MFC DLLs for Beginners
1.1、創建用於導出dll的工程
1.1.1、創建項目
A. 選擇文件->新建->項目。
B. 選擇模板MFC DLL,名稱填寫為MyDll,如圖1.1。
C. 選擇使用共享MFC DLL的規則DLL,如圖1.2。
1.1.2、添加要導出的類與函數
A. 類視圖中,選中MyDll,右鍵->添加->類,選擇C++ 類,如圖1.3。
B. 類的名稱為CMyClass,如圖1.4。
C. 類視圖中,選中CMyClass,右鍵->添加->函數。
D. 返回類型CString,函數名SayHello,參數CString strName,如圖1.5。
E. 實現SayHello
// CMyClass.cpp
CString CMyClass::SayHello(CString strName)
{
return "Hello " + strName;
}
1.1.3、將類中的函數聲明為要導出的類型
為CMyClass()、~CMyClass()、SayHello()添加__declspec(dllexport)
// CMyClass.h
class CMyClass
{
public:
__declspec(dllexport) CMyClass(void);
__declspec(dllexport) ~CMyClass(void);
__declspec(dllexport) CString SayHello(CString strName);
};
1.1.4、編譯
選擇Release模式,進行編譯,得到MyDll.dll和MyDll.lib。
如果有錯誤,可以參考以下解決方案: 報錯: **1 error C2678: 二進制“+”: 沒有找到接受“const char [7]”類型的左操作數的運算符(或沒有可接受的轉換) f:\MyDll\MyDll\MyClass.cpp 14**。 解決: 選中**MyDll**,右鍵->**屬性**->**配置屬性**->**常規**->**字符集**->**使用多字節字符集**。1.2、創建調用dll的工程
1.2.1、創建項目
A. 選擇文件->新建->項目。
B. 選擇模板MFC 應用程序,名稱填寫為TestDLL,如圖1.6。
C. 應用程序類型選擇基於對話框,點擊完成,如圖1.7。
1.2.2、修改UI界面
A. 點擊Static Text控件,在屬性窗口中修改Caption的值為"Enter your name and click ok"。
B. 在工具箱窗口中,拖動一個Edit Control 控件到界面上,如圖1.8。
1.2.3、給Edit Control關聯一個變量
A. 選中Edit Control控件,右鍵->添加變量。
B. 變量類型填寫CString,變量名為m_edit,類別為Value,如圖1.9。
1.2.4、給Button綁定一個單擊事件
A. 選中確定按鈕,雙擊,自動創建新函數void CTestDLLDlg::OnBnClickedOk()
1.2.5、導入dll
A. 在工程目錄下創建include文件夾和lib文件夾,如圖1.10。
將MyClass.h復制到include文件下,將MyDll.lib復制到lib文件夾下
B. 切換解決方案配置為Release
C. 選中TestDLL,右鍵->屬性->C/C++->常規->附加包含目錄,添加../include,如圖1.11。
D. 選中TestDLL,右鍵->屬性->鏈接器->常規->附加庫目錄,添加../lib,如圖1.12。
1.2.6、添加頭文件和lib文件
// TestDLLDlg.h
#include "MyClass.h"
#pragma comment(lib, "MyDll.lib")
1.2.7、創建MyClass對象
在CTestDLLDlg類中添加CMyClass objMyClass
// TestDLLDlg.h
class CTestDLLDlg : public CDialog
{
// 構造
public:
CTestDLLDlg(CWnd* pParent = NULL); // 標准構造函數
CMyClass objMyClass;
1.2.8、實現確定單擊事件OnBnClickedOk
// TestDLLDlg.cpp
void CTestDLLDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(true);
CString strResult = objMyClass.SayHello(m_edit);
AfxMessageBox (strResult);
//OnOK();
}
1.3、編譯運行
A. 選擇Release模式,進行編譯,得到TestDLL.exe。
如果有錯誤,可以參考以下解決方案: 報錯: **1>TestDLLDlg.obj : error LNK2001: 無法解析的外部符號 "public: class ATL::CStringTB. 將MyDll.dll拷貝TestDLL.exe到同級目錄下,單擊TestDLL.exe運行,如圖1.13。
2、知識點
2.1、MFC的dll工程
-
MFC編譯dll在創建項目時已經提供了模板MFC DLL。
-
在MFC DLL模板中有共享MFC DLL的規則DLL 、帶靜態鏈接MFC的規則DLL和MFC擴展DLL三種。選擇共享MFC DLL的規則DLL后,在運行該dll時,機器必須安裝MFC庫。選擇帶靜態鏈接MFC的規則DLL,在運行該dll時不需要安裝MFC庫。
2.2、導出dll函數的兩種方式
- 方法一:函數聲明時標注_declspec(dllexport)表示導出函數
例如:
__declspec(dllexport) CString SayHello(CString strName);
- 方法二:.def文件中標注導出函數名
例如:
; DLL.def : 聲明 DLL 的模塊參數。
LIBRARY "DLL"
EXPORTS
; 此處可以是顯式導出
SayHello
2.2、_declspec(dllexport)與.def文件的異同
-
對於VC++程序調用dll兩者沒有區別,對於其他(例如VB)程序調用dll最好用.def方式。
-
對於類的導出,往往使用_declspec(dllexport)方式
2.3、__declspec(dllexport)和__declspec(dllimport)的區別
該部分摘自[__declspec(dllexport) & __declspec(dllimport)](http://www.cnblogs.com/xd502djj/archive/2010/09/21/1832493.html)-
__declspec(dllexport)
聲明一個導出函數,是說這個函數要從本DLL導出。我要給別人用。一般用於dll中。
省掉在DEF文件中手工定義導出哪些函數的一個方法。當然,如果你的DLL里全是C++的類的話,你無法在DEF里指定導出的函數,只能用__declspec(dllexport)導出類。 -
__declspec(dllimport)
聲明一個導入函數,是說這個函數是從別的DLL導入。我要用。一般用於使用某個dll的exe中。
不使用 __declspec(dllimport) 也能正確編譯代碼,但使用 __declspec(dllimport) 使編譯器可以生成更好的代碼。編譯器之所以能夠生成更好的代碼,是因為它可以確定函數是否存在於 DLL 中,這使得編譯器可以生成跳過間接尋址級別的代碼,而這些代碼通常會出現在跨 DLL 邊界的函數調用中。但是,必須使用 __declspec(dllimport) 才能導入 DLL 中使用的變量。
2.4、_declspec(dllexport)兼容C的通常使用方式
A. 宏定義__declspec(dllexport)
#ifdef TOOL_EXPORTS
#define TOOL_API extern "C" __declspec(dllexport)
#else
#define TOOL_API extern "C" __declspec(dllimport)
#endif
TOOL_API int Test(int a,int b);
B. 在工程中定義宏TOOL_EXPORTS
點擊工程,右鍵屬性->C/C++->預處理器->預處理器定義,添加TOOL_EXPORTS,如圖2.1