亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

假設(shè)被調(diào)用的DLL存在一個(gè)導(dǎo)出函數(shù),原型如下:

void printN(int);

1|0三種方式從DLL導(dǎo)入導(dǎo)出函數(shù)

  • 生成DLL時(shí)使用模塊定義 (.def) 文件
  • 在主應(yīng)用程序的函數(shù)定義中使用關(guān)鍵字__declspec(dllimport)或__declspec(dllexport)
  • 利用#pragma comment(linker, "/export:[Exports Name]=[Mangling Name]"

def編寫規(guī)范:參考模塊定義 (.Def) 文件

基本規(guī)則:

  • LIBRARY 語(yǔ)句說(shuō)明 .def ?件相應(yīng)的 DLL;
  • EXPORTS 語(yǔ)句后列出要導(dǎo)出函數(shù)的名稱。可以在 .def ?件中的導(dǎo)出函數(shù)名后加 @n,表 示要導(dǎo)出函數(shù)的序號(hào)為 n(在進(jìn)?函數(shù)調(diào)?時(shí),這個(gè)序號(hào)將發(fā)揮其作?);
  • .def ?件中的注釋由每個(gè)注釋?開始處的分號(hào) ( 指定,且注釋不能與語(yǔ)句共享??。

2|0編寫dll注意點(diǎn)

編寫dll時(shí),有個(gè)重要的問(wèn)題需要解決,那就是函數(shù)重命名——Name-Mangling。解決方式有兩種,一種是直接在代碼里解決采用extent”c”、_declspec(dllexport)、#pragma comment(linker, "/export:[Exports Name]=[Mangling Name]"),另一種是采用def文件。

2|1編寫dll時(shí),為什么有 extern “C”

原因:因?yàn)镃和C++的重命名規(guī)則是不一樣的。這種重命名稱為“Name-Mangling”(名字修飾或名字改編、標(biāo)識(shí)符重命名,有些人翻譯為“名字粉碎法”,這翻譯顯得有些莫名其妙)

據(jù)說(shuō),C++標(biāo)準(zhǔn)并沒(méi)有規(guī)定Name-Mangling的方案,所以不同編譯器使用的是不同的,例如:Borland C++跟Mircrosoft C++就不同,而且可能不同版本的編譯器他們的Name-Mangling規(guī)則也是不同的。這樣的話,不同編譯器編譯出來(lái)的目標(biāo)文件.obj 是不通用的,因?yàn)橥粋€(gè)函數(shù),使用不同的Name-Mangling在obj文件中就會(huì)有不同的名字。如果DLL里的函數(shù)重命名規(guī)則跟DLL的使用者采用的重命名規(guī)則不一致,那就會(huì)找不到這個(gè)函數(shù)。

影響符號(hào)名的除了C++和C的區(qū)別、編譯器的區(qū)別之外,還要考慮調(diào)用約定導(dǎo)致的Name Mangling。如extern “c” __stdcall的調(diào)用方式就會(huì)在原來(lái)函數(shù)名上加上寫表示參數(shù)的符號(hào),而extern “c” __cdecl則不會(huì)附加額外的符號(hào)。

dll中的函數(shù)在被調(diào)用時(shí)是以函數(shù)名或函數(shù)編號(hào)的方式被索引的。這就意味著采用某編譯器的C++的Name-Mangling方式產(chǎn)生的dll文件可能不通用。因?yàn)樗鼈兊暮瘮?shù)名重命名方式不同。為了使得dll可以通用些,很多時(shí)候都要使用C的Name-Mangling方式,即是對(duì)每一個(gè)導(dǎo)出函數(shù)聲明為extern “C”,而且采用_stdcall調(diào)用約定,接著還需要對(duì)導(dǎo)出函數(shù)進(jìn)行重命名,以便導(dǎo)出不加修飾的函數(shù)名。

注意到extern “C”的作用是為了解決函數(shù)符號(hào)名的問(wèn)題,這對(duì)于動(dòng)態(tài)鏈接庫(kù)的制造者和動(dòng)態(tài)鏈接庫(kù)的使用者都需要遵守的規(guī)則。

動(dòng)態(tài)鏈接庫(kù)的顯式裝入就是通過(guò)GetProcAddress函數(shù),依據(jù)動(dòng)態(tài)鏈接庫(kù)句柄和函數(shù)名,獲取函數(shù)地址。因?yàn)镚etProcAddress僅是操作系統(tǒng)相關(guān),可能會(huì)操作各種各樣的編譯器產(chǎn)生的dll,它的參數(shù)里的函數(shù)名是原原本本的函數(shù)名,沒(méi)有任何修飾,所以一般情況下需要確保dll里的函數(shù)名是原始的函數(shù)名。分兩步:
一,如果導(dǎo)出函數(shù)使用了extern”C” _cdecl,那么就不需要再重命名了,這個(gè)時(shí)候dll里的名字就是原始名字;如果使用了extern”C” _stdcall,這時(shí)候dll中的函數(shù)名被修飾了,就需要重命名。
二、重命名的方式有兩種,要么使用*.def文件,在文件外修正,要么使用#pragma,在代碼里給函數(shù)別名。

2|2_declspec(dllexport)和_declspec(dllimport)的作用

_declspec還有另外的用途,這里只討論跟dll相關(guān)的使用。正如括號(hào)里的關(guān)鍵字一樣,導(dǎo)出和導(dǎo)入。_declspec(dllexport)用在dll上,用于說(shuō)明這是導(dǎo)出的函數(shù)。而_declspec(dllimport)用在調(diào)用dll的程序中,用于說(shuō)明這是從dll中導(dǎo)入的函數(shù)。

因?yàn)閐ll中必須說(shuō)明函數(shù)要用于導(dǎo)出,所以_declspec(dllexport)很有必要。但是可以換一種方式,可以使用def文件來(lái)說(shuō)明哪些函數(shù)用于導(dǎo)出,同時(shí)def文件里邊還有函數(shù)的編號(hào)。

而使用_declspec(dllimport)卻不是必須的,但是建議這么做。因?yàn)槿绻挥胈declspec(dllimport)來(lái)說(shuō)明該函數(shù)是從dll導(dǎo)入的,那么編譯器就不知道這個(gè)函數(shù)到底在哪里,生成的exe里會(huì)有一個(gè)call XX的指令,這個(gè)XX是一個(gè)常數(shù)地址,XX地址處是一個(gè)jmp dword ptr[XXXX]的指令,跳轉(zhuǎn)到該函數(shù)的函數(shù)體處,顯然這樣就無(wú)緣無(wú)故多了一次中間的跳轉(zhuǎn)。如果使用了_declspec(dllimport)來(lái)說(shuō)明,那么就直接產(chǎn)生call dword ptr[XXX],這樣就不會(huì)有多余的跳轉(zhuǎn)了。

2|3__stdcall帶來(lái)的影響

這是一種函數(shù)的調(diào)用方式。默認(rèn)情況下VC使用的是__cdecl的函數(shù)調(diào)用方式,如果產(chǎn)生的dll只會(huì)給C/C++程序使用,那么就沒(méi)必要定義為__stdcall調(diào)用方式,如果要給Win32匯編使用(或者其他的__stdcall調(diào)用方式的程序),那么就可以使用__stdcall。這個(gè)可能不是很重要,因?yàn)榭梢宰约涸谡{(diào)用函數(shù)的時(shí)候設(shè)置函數(shù)調(diào)用的規(guī)則。像VC就可以設(shè)置函數(shù)的調(diào)用方式,所以可以方便的使用win32匯編產(chǎn)生的dll。不過(guò)__stdcall這調(diào)用約定會(huì)Name-Mangling,所以我覺(jué)得用VC默認(rèn)的調(diào)用約定簡(jiǎn)便些。但是,如果既要__stdcall調(diào)用約定,又要函數(shù)名不給修飾,那可以使用*.def文件,或者在代碼里#pragma的方式給函數(shù)提供別名(這種方式需要知道修飾后的函數(shù)名是什么)。

舉例:

·extern “C” __declspec(dllexport) bool  __stdcall cswuyg();
·extern “C”__declspec(dllimport) bool __stdcall cswuyg();

·#pragma comment(linker, "/export:cswuyg=_cswuyg@0")

3|0編寫測(cè)試dll代碼

項(xiàng)目結(jié)構(gòu):

exe調(diào)用DLL的方式

 

cpp源代碼:

 #include <IOStream>
using namespace std;

extern "C" {
	_declspec(dllexport) void printN(int n)
	{
		//printf("%dn", n);
		cout << n << endl;
	}
}

void printM(int m)
{
	cout << m << endl;
}

#pragma comment(linker, "/export:getNresult=?getNresult@@YAHXZ")
int getNresult()
{
	//printf("%dn", n);
	return 123;
}

def代碼:

LIBRARY DLLTEST
EXPORTS
	printM

項(xiàng)目屬性中將配置類型改為dll:

exe調(diào)用DLL的方式

 

模塊定義文件改為dlltest.def:

exe調(diào)用DLL的方式

 

編譯之后,使用CFF Explorer查看導(dǎo)出函數(shù):

exe調(diào)用DLL的方式

 

其中printN函數(shù)用extern "C" _declspec(dllexport)的方式導(dǎo)出,避免了函數(shù)名粉碎;
printM函數(shù)用def的形式導(dǎo)出,也避免了函數(shù)名粉碎;
getNresult函數(shù)用#pragma comment(linker, "/export:getNresult=?getNresult@@YAHXZ")的形式避免了函數(shù)名粉碎,但是需要知道粉碎后的原始函數(shù)符號(hào);

這里涉及一個(gè)問(wèn)題,原始函數(shù)符號(hào)怎么找到的,方法是先用_declspec(dllexport)方式導(dǎo)出,然后編譯后利用CFF即可看到原始函數(shù)符號(hào)。

exe調(diào)用DLL的方式

 

編譯dll后會(huì)產(chǎn)生一個(gè)dll文件和一個(gè)lib文件,如果是運(yùn)行時(shí)動(dòng)態(tài)調(diào)用的方式只使用dll文件就行,如果要在編譯時(shí)以庫(kù)的形式提供給exe調(diào)用則需要lib文件。

4|0編寫exe調(diào)用dll

項(xiàng)目結(jié)構(gòu):

exe調(diào)用DLL的方式

 

cpp源碼:

#include <iostream>
using namespace std;
#pragma comment(lib, "C:\project\dlltest\Debug\dlltest.lib")

extern "C" __declspec(dllimport) void printN(int);
int getNresult();
void printM(int);
int main()
{
	printN(123);
	printM(12);
	cout << getNresult() << endl;
	return 0;
}

在#pragma中更改為自己的lib路徑,printN與extern "C" __declspec(dllimport)形式導(dǎo)入,getNresult和printM是c++格式的,應(yīng)該使用__declspec(dllimport)導(dǎo)入,不過(guò)導(dǎo)入函數(shù)的情況下可以省略不寫,引用外部變量則不能省略。

執(zhí)行結(jié)果:

exe調(diào)用DLL的方式

 

5|0利用LoadLibrary動(dòng)態(tài)加載dll的方式

這種方式需要明確指定dll的位置,而不是程序根據(jù)環(huán)境變量配置自己尋找(上面的方式中并沒(méi)有指明dll的位置,exe和dll同目錄會(huì)自動(dòng)搜索加載)。

代碼:

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

int main()
{
	HINSTANCE h = LoadLibrary(L"C:\project\dlltest\Debug\dlltest.dll");
	if (h == NULL)
	{
		cout << "dll加載失敗!" << endl;
	}
	else
	{
		void* func = GetProcAddress(h, "printN");
		if (func != NULL)
		{
			((void(*)(int))func)(2);
		}
		else
		{
			cout << "未找到相關(guān)函數(shù)!" << endl;
		}
	}
	return 0;
}

需要注意將項(xiàng)目的字符集改為Unicode:

exe調(diào)用DLL的方式

分享到:
標(biāo)簽:調(diào)用 DLL
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定