創建動態鏈接庫 (DLL) 項目
-------參考MSDN和Repeaterbin的專欄
- 在菜單欄上,選擇“文件”,“新建、“項目”。
- 在“新建項目”對話框左窗格中,展開“已安裝”、“模板”、“Visual C++”,然後選擇“Win32”。
- 在中間窗格中,選擇“Win32 控制檯應用程序”。
- 在“名稱”框中爲項目指定名稱,例如,MathFuncsDll。 在“解決方案名稱”框中爲解決方案指定一個名稱,例如 DynamicLibrary。 選擇“確定”按鈕。
- 在“Win32 應用程序嚮導”對話框的“概述”頁上,選擇“下一步”按鈕。
- 在“應用程序設置”頁上的“應用程序類型”下,選擇“DLL”。
- 選擇“完成”按鈕創建項目。
向動態鏈接庫添加類
- 若要爲新類創建頭文件,在菜單欄上依次選擇“項目”、“添加新項...”。 在“添加新項”對話框中,在左窗格的“Visual C++”下,選擇“代碼”。 在中心窗格中,選擇“頭文件 (.h)”。 爲頭文件指定一個名稱,例如 MathFuncsDll.h,然後選擇“添加”按鈕。 將顯示一個空白頭文件。
- 添加下列代碼至頭文件的開頭:
- 添加下列代碼至頭文件的開頭:
// MathFuncsDll.h
#ifdef MATHFUNCSDLL_EXPORTS
#define MATHFUNCSDLL_API __declspec(dllexport)
#else
#define MATHFUNCSDLL_API __declspec(dllimport)
#endif
- 在MathFuncsDll.h中,添加一個名爲“MyMathFuncs”的基礎類,以執行常見的算術運算,如加、減、乘和除。 代碼應類似如下:
namespace MathFuncs
{
// This class is exported from the MathFuncsDll.dll
class MyMathFuncs
{
public:
// Returns a + b
static MATHFUNCSDLL_API double Add(double a, double b);
// Returns a - b
static MATHFUNCSDLL_API double Subtract(double a, double b);
// Returns a * b
static MATHFUNCSDLL_API double Multiply(double a, double b);
// Returns a / b
// Throws const std::invalid_argument& if b is 0
static MATHFUNCSDLL_API double Divide(double a, double b);
};
}
- 當定義了 MATHFUNCSDLL_EXPORTS 符號時,MATHFUNCSDLL_API 符號將在此代碼中的成員函數聲明中設置 __declspec(dllexport) 修飾符。 此修飾符使函數能作爲 DLL 導出,以供其他應用程序調用。 當 MATHFUNCSDLL_EXPORTS 未定義時,MATHFUNCSDLL_API 會在成員函數聲明中定義 __declspec(dllimport) 修飾符。 此修飾符能夠使編譯器優化從 DLL 導入的用於其他應用程序的函數。 默認情況下,生成 MathFuncsDll 項目時會定義 MATHFUNCSDLL_EXPORTS。在“解決方案資源管理器”的“MathFuncsDll”項目中,在“源文件”文件夾中,打開“MathFuncsDll.cpp”。
- 在“解決方案資源管理器”的“MathFuncsDll”項目中,在“源文件”文件夾中,(存在)打開或(不存在)添加“MathFuncsDll.cpp”、
// MathFuncsDll.cpp : Defines the exported functions for the DLLapplication.
#include "stdafx.h"
#include "MathFuncsDll.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;
}
}
備註:導出函數常用方式:
1,使用DEF文件從DLL中導出
.def 文件必須至少包含下列模塊定義語句:
文件中的第一個語句必須是 LIBRARY 語句。 此語句將 .def 文件標識爲屬於 DLL。 LIBRARY 語句的後面是 DLL 的名稱。 鏈接器將此名稱放到 DLL 的導入庫中。
EXPORTS 語句列出名稱,可能的話還會列出 DLL 導出函數的序號值。 通過在函數名的後面加上 @ 符和一個數字,給函數分配序號值。 當指定序號值時,序號值的範圍必須是從 1 到N,其中 N 是 DLL 導出函數的個數。
LIBRARY BTREE
EXPORTS
Insert @1
Delete @2
Member @3
Min @4
2,使用__declspec(dllexport) 從DLL 導出
可以使用 __declspec(dllexport) 關鍵字從 DLL 導出數據、函數、類或類成員函數。 __declspec(dllexport) 會將導出指令添加到對象文件中,因此不需要使用 .def 文件。
若要導出函數,__declspec(dllexport) 關鍵字必須出現在調用約定關鍵字的左邊(如果指定了關鍵字)。
__declspec(dllexport) void __cdecl Function1(void);
若要導出類中的所有公共數據成員和成員函數,關鍵字必須出現在類名的左邊,
class__declspec(dllexport) CExampleExport
{ ... classdefinition ... };
我們常用的導出定義:
#ifdef _EXPORTING
#defineAPI_DECLSPEC __declspec(dllexport)
#else
#defineAPI_DECLSPEC __declspec(dllimport)
#endif
class API_DECLSPECCBtt
{
public:
CBtt(void);
~CBtt(void);
public:
CString m_str;
static int GetValue()
{
return m_nValue;
}
private:
static int m_nValue;
};
注:單獨使用__declspec(dllexport)已經可以實現DLL中庫函數的導出,不使用 __declspec(dllimport)也能正確編譯代碼,但使用 __declspec(dllimport)使編譯器可以生成更好的代碼。編譯器之所以能夠生成更好的代碼,是因爲它可以確定函數是否存在於 DLL 中,這使得編譯器可以生成跳過間接尋址級別的代碼,而這些代碼通常會出現在跨 DLL 邊界的函數調用中。但是,必須使用 __declspec(dllimport)才能導入 DLL 中使用的變量。
__declspec(dllimport)的實際用處:
SIMPLEDLL_EXPORT
SimpleDLLClass.h
#ifdefSIMPLEDLL_EXPORT
#define DLL_EXPORT__declspec(dllexport)
#else
#define DLL_EXPORT
#endif
class DLL_EXPORTSimpleDLLClass
{
public:
SimpleDLLClass();
virtual ~SimpleDLLClass();
virtual getValue() { return m_nValue;};
private:
int m_nValue;
};
SimpleDLLClass.cpp
#include"SimpleDLLClass.h"
SimpleDLLClass::SimpleDLLClass()
{
m_nValue=0;
}
SimpleDLLClass::~SimpleDLLClass()
{
}
然後你再使用這個DLL類,在你的項目中include SimpleDLLClass.h時,你的項目不用定義 SIMPLEDLL_EXPORT 所以,DLL_EXPORT 就不會存在了,這個時候,不會遇到問題。這正好對應MSDN上說的__declspec(dllimport)定義與否都可以正常使用。但,我們改一下SimpleDLLClass,把它的m_nValue改成static,然後在cpp文件中加一行
int SimpleDLLClass::m_nValue=0;
改完之後,再去LINK一下,你的APP,看結果如何,結果是LINK告訴你找不到這個m_nValue。
改一下SimpleDLLClass.h:
#ifdefSIMPLEDLL_EXPORT
#define DLL_EXPORT__declspec(dllexport)
#else
#define DLL_EXPORT__declspec(dllimport)
#endif
再LINK,一切正常。原來dllimport是爲了更好的處理類中的靜態成員變量的,如果沒有靜態成員變量,那麼這個__declspec(dllimport)無所謂。