1. 程式人生 > >VS2012 建立和使用DLL

VS2012 建立和使用DLL

1.為什麼使用DLL?

我們知道提高開發效率的一個很重要的途徑就是程式碼複用,我們經常將一些常用的功能構造成相對獨立的模組,並在之後的專案重複使用,程式碼複用的方式有兩種:

  • 白盒複用:如ATL、MFC等,它們都以原始碼的形式釋出,原始碼完全暴露給了程式設計師。
白盒複用的缺點比較多,暴露原始碼不說,容易與我們自己的程式碼產生命名衝突,還有就是更新功能比較麻煩。
  • 黑盒複用:如Dll,靜態連結,com元件等。與白盒複用相比,dll黑盒複用的優勢就很明顯,dll是二進位制檔案,
因此隱藏了原始碼,如果採用“顯式呼叫,一般就能避免命名衝突;.dll檔案相對獨立的存在,因此更新功能模組是可行的。

2.如何建立一個DLL專案?

用vs2012建立一個新的Win32 應用控制檯程式,名字取為math,要勾選dll選項。

如圖:


然後新建mymath.h和mymath.cpp檔案。
mymath.h如下:

#pragma once

#ifdef MATH_EXPORTS
#define MATH_API __declspec(dllexport)
#else
#define MATH_API __declspec(dllimport)
#endif

namespace shun
{
	class MATH_API Cmath {				//類
	public:
		int add(int a,int b);
	};

	extern "C" MATH_API float pi;			//變數

	extern "C" MATH_API int getMax(int &,int &);	//函式
}

mymath.cpp如下:
#include "stdafx.h"
#include "mymath.h"

namespace shun
{
	float pi=3.1415;			//變數

	int getMax(int& a, int& b) //函式
	{
		return a > b ? a : b;
	}

	int Cmath::add(int a,int b)			//類方法
	{
		return a + b;
	}
}

然後生成解決方案,編譯成功後可以在debug資料夾下發現math.dll和math.lib檔案。
至此,我們已經成功建立一個dll專案了。


3.如何隱式引用
一個DLL專案?

隱式呼叫三要素,.h檔案 ,.dll檔案,.lib檔案,缺一不可。
隱式呼叫的好處是可以像呼叫本地函式一樣方便。


3.1如果專案和dll專案在同一個解決方案

為此我們新建一個win32控制檯的空專案,取名useMath,然後引用dll步驟如下:
  1. 專案->屬性->通用屬性->框架和引用->新增新引用->勾選math專案->確定
  2. 專案->屬性->配置屬性->VC++目錄->包含目錄->新增mymath.h所在的目錄

然後新增一個test.cpp檔案,輸入如下:

#include <iostream>  
#include "mymath.h"

using namespace std;  

int main(char argc, char**argv)  
{  
	int a = 1, b = 2;

	//類的使用
	shun::Cmath cm;
	cout<<cm.add(1,2)<<endl; 
	//變數
	cout<<shun::pi<<endl;
	//函式
	cout<<shun::getMax(a,b);  
	getchar();  
	return 0;  
}  
執行一下我們發現呼叫dll專案了有木有。。

3.2如果專案和dll專案不在同一個解決方案呢?

為此,我們先開啟一個新的vs2012,新建一個win32控制檯的空專案,取名useMath,然後引用dll步驟如下:
  1. 專案->屬性->配置屬性->VC++ 目錄-> 在“包含目錄”裡新增標頭檔案mymath.h所在的目錄 
  2. 專案->屬性->配置屬性->VC++ 目錄-> 在“庫目錄”裡新增標頭檔案math.lib所在的目錄 
  3. 專案->屬性->配置屬性->連結器->輸入-> 在“附加依賴項”裡新增“math.lib”(若有多個 lib 則以空格隔開)
  4. 將dll專案下的debug檔案中的math.dll複製到當前專案的debug資料夾中
新建然後新增一個test.cpp檔案,輸入如下:(和之前一樣)
#include <iostream>  
#include "mymath.h"

using namespace std;  

int main(char argc, char**argv)  
{  
	int a = 1, b = 2;

	//類的使用
	shun::Cmath cm;
	cout<<cm.add(1,2)<<endl; 
	//變數
	cout<<shun::pi<<endl;
	//函式
	cout<<shun::getMax(a,b);  
	getchar();  
	return 0;  
}  

4.如何顯示呼叫dll專案?

顯示呼叫只需要一個dll檔案。顯示呼叫好處是模組相對獨立,更新非常方便。不好的地方是使用起來稍微複雜。
同之前一樣,我們新建一個win32控制檯專案,然後將math.dll放入debug資料夾中,

因為需要使用到windows api,所以要引入windows.h標頭檔案。

新增test.cpp如下:

#include <windows.h>
#include <iostream>
using namespace std;

typedef int (*Func)(int &, int &);

int main(int argc, char *argv[])
{
	int a = 5, b = 10;
	HMODULE hDll = LoadLibrary("math.dll");
	if (hDll != NULL)
	{
		Func getMax = (Func)GetProcAddress(hDll, "getMax");	//函式
		if (getMax != NULL)
		{
			cout<<getMax(a, b)<<endl;
		}
		float* pPi = (float*)GetProcAddress(hDll,"pi");		//變數
		if(pPi != NULL)
		{
			cout<<*pPi<<endl;
		}

		FreeLibrary(hDll);
	}
	getchar();
}

如果對於函式指標不太熟悉的話可以點這裡:

如果要顯示呼叫類的方法的話,可以在dll中寫一個函式,由這個函式去呼叫,比如像這樣子:
.
//.h檔案
extern "C" MATH_API int add_Interface(int &,int &);	//函式
//.cpp檔案
int add_Interface(int &a,int &b)
{
	Cmath cm;
	return cm.add(a,b);
}

然後像上面呼叫函式一樣呼叫add_Interface。

5.extern "C"作用

我們知道,extern可以置於變數或者函式前,以標示變數或者函式的定義在別的檔案中,提示編譯器遇到此變數和函式時在其他模組中尋找其定義。

C++語言在編譯的時候為了解決函式的多型問題,會將函式名和引數聯合起來生成一箇中間的函式名稱。
比如:

void foo( int x, int y );
該函式被C編譯器編譯後在符號庫中的名字為_foo,而C++編譯器則會產生像_foo_int_int之類的名字(不同的編譯器可能生成的名字不同)。

所以我們可以在函式前使用extern "C"來告訴編譯器要採用c編譯的方式,而不是c++編譯的方式。