
相關測試代碼
代碼複用的實作:
1.靜态連結庫
一,建立靜态連結庫:
(1)在VC6中建立項目:Win32 Static Library
(2)在項目中建立兩個檔案:cntftools.h 和 cntftools.cpp; 這裡建立兩個檔案就是上面操作完成之後,直接建立一個class即可生成這兩檔案;
cntftools.h檔案:
#if !defined(AFX_TEST_H__DB32E837_3E66_4BE7_B873_C079BC621AF0__INCLUDED_)
#define AFX_TEST_H__DB32E837_3E66_4BE7_B873_C079BC621AF0__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
int Plus(int x, int y);
int Sub(int x, int y);
int Mul(int x, int y);
int Div(int x, int y);
#endif
cntftools.cpp檔案:
int Plus(int x, int y)
{
return x+y;
}
int Sub(int x, int y)
{
return x-y;
}
int Mul(int x, int y)
{
return x*y;
}
int Div(int x, int y)
{
return x/y;
}
3.編譯 --- 點選編譯即可,不需要執行,調試等其他操作;
二,使用靜态連結庫:
方式一:
(1)将上面編譯完成之後,生成的cntftools.h 和 cntflibs.lib複制到要使用的項目中,這裡放的位置是生成項目的檔案夾,不是debug
檔案夾裡面;
(2)在需要使用的檔案中包含:#include "cntftools.h"
(3)在需要使用的檔案中包含:#pragma comment(lib, "cntflibs.lib")
(4)下面是重新生成的一個項目檔案,然後放入上面的頭檔案和lib檔案,編譯成功可正常執行,下面是對應的代碼
// sjlx.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdlib.h>
#include "cntftools.h"
#pragma comment(lib,"cntflibs.lib")
int main()
{
int x = Plus(2,3);
printf("%d\r\n",x);
system("pause");
return 0;
}
方式二:
(1)将上面編譯完成之後,生成的cntftools.h 和 cntflibs.lib複制到要使用的項目中,這裡放的位置是生成項目的檔案夾,不是debug
檔案夾裡面;
(2)在需要使用的檔案中包含:#include "cntftools.h";
(3)在項目名稱中右鍵-->設定-Link-->找到Object/library Module 在最後面空格一下,添加cntflibs.lib;下面是最終的結果;
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib、
advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib cntflibs.lib
(4)再次編譯執行,可成功運作;
三,靜态連結庫的缺點:
(1)使用靜态連結生成的可執行檔案體積較大,造成浪費;
(2)我們常用的printf、memcpy、strcpy等就來自這種靜态庫 ;
(3)最重要的原因就是因為生成的函數對應代碼是在變成成功之後的exe裡面,是以如果要更改lib的話,很麻煩,需要;
需要再次重新編譯生成exe檔案;驗證這一點,可以通過執行此函數的過程中檢視反彙編代碼;發現生成的反彙編代碼;
其對應的記憶體位址是帶入了ImageBase:0x00400000;
2.動态連結庫
一,建立DLL
(1)在VC6中建立項目:Win32 Dynamic-Link Library ,名稱寫dtljkcntf
(2)再在項目中建立兩個檔案:mydll.h 和 mydll.cpp; 這裡建立兩個檔案就是,直接建立一個class即可生成這兩檔案
1.源檔案中
int __stdcall Plus(int x,int y)
{
return x+y;
}
int __stdcall Sub(int x,int y)
{
return x-y;
}
int __stdcall Mul(int x,int y)
{
return x*y;
}
int __stdcall Div(int x,int y)
{
return x/y;
}
2.頭檔案中
extern "C" _declspec(dllexport) __stdcall int Plus (int x,int y);
extern "C" _declspec(dllexport) __stdcall int Sub (int x,int y);
extern "C" _declspec(dllexport) __stdcall int Mul (int x,int y);
extern "C" _declspec(dllexport) __stdcall int Div (int x,int y);
說明:
1.extern 表示這是個全局函數,可以供各個其他的函數調用;
2."C" 按照C語言的方式進行編譯、連結
__declspec(dllexport)告訴編譯器此函數為導出函數;
二,使用DLL
方式一:隐式連接配接
步驟1:将 *.dll *.lib 放到工程目錄下面
步驟2:将 #pragma comment(lib,"DLL名.lib") 添加到調用檔案中
步驟3:加入函數的聲明 --這裡就是加在項目對應的主程式代碼裡面,我這裡測試就在main函數入口的代碼上面;
extern "C" __declspec(dllimport) __stdcall int Plus (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Sub (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Mul (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Div (int x,int y);
說明:
__declspec(dllimport)告訴編譯器此函數為導入函數;
下面是成功執行的主測試代碼;
#include "stdafx.h"
#include <stdlib.h>
__declspec(dllimport) int Plus (int x,int y);
__declspec(dllimport) int Sub (int x,int y);
__declspec(dllimport) int Mul (int x,int y);
__declspec(dllimport) int Div (int x,int y);
#pragma comment(lib,"dtljkcntf.lib")
int main()
{
int x = Plus(2,3);
printf("%d\r\n",x);
system("pause");
return 0;
}
上面需要注意的地方:
因為我測試編譯生成dll和lib檔案之前寫的代碼是沒有帶入extern "C" 和__stdcall這兩個關鍵字;
當然上面是為了測試,實際情況需要帶入是最好的;
測試結果是告訴我們,如果不帶入上面兩個關鍵字,那麼編譯生成的函數名稱編譯器會給我們重新命名;
簡單了解操作就會給添加一些奇怪的字元,目的就是為了防止函數重名,因為在C++裡面有重載的說法;
如果函數重名會導緻其他異常情況;
是以總結一下:在生成dll和lib之前我們寫的什麼關鍵字代碼,那麼這裡就要寫什麼;
檢視dll檔案顯示函數資訊:
使用工具可以是微軟VC6.0 ++ 自帶的Dependency進行檢視;
也可以用OD檢視,點選按鈕"E"; 使用OD檢視的時候,記得要要把上面生成好的dtljkcntf.dll檔案放在;
可執行程式exe的相同目錄下測試檢視驗證;
下面是帶入extern "C" 和__stdcall關鍵字的操作;
#include "stdafx.h"
#include <stdlib.h>
extern "C" __declspec(dllimport) __stdcall int Plus (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Sub (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Mul (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Div (int x,int y);
#pragma comment(lib,"dtljkcntf.lib")
int main()
{
int x = Plus(998,663);
printf("%d\r\n",x);
system("pause");
return 0;
}
方式二:顯示連結
步驟1: //定義函數指針
typedef int (__stdcall *lpPlus)(int,int);
typedef int (__stdcall *lpSub)(int,int);
typedef int (__stdcall *lpMul)(int,int);
typedef int (__stdcall *lpDiv)(int,int);
步驟2: //聲明函數指針變量
lpPlus myPlus;
lpSub mySub;
lpMul myMul;
lpDiv myDiv;
步驟3: // //動态加載dll到記憶體中
HINSTANCE hModule = LoadLibrary("DllDemo.dll");
步驟4: //擷取函數位址
myPlus = (lpPlus)GetProcAddress(hModule, "_Plus@8");
mySub = (lpSub)GetProcAddress(hModule, "_Sub@8");
myMul = (lpMul)GetProcAddress(hModule, "_Mul@8");
myDiv = (lpDiv)GetProcAddress(hModule, "_Div@8");
步驟5: //調用函數
int a = myPlus(10,2);
int b = mySub(10,2);
int c = myMul(10,2);
int d = myDiv(10,2);
上述操作完成之後,對應的可執行代碼如下:
#include "stdafx.h"
#include <stdlib.h>
#include <windows.h>
//#include <string.h>
//定義函數指針;
typedef int (__stdcall *lpPlus)(int,int);
typedef int (__stdcall *lpSub)(int,int);
typedef int (__stdcall *lpMul)(int,int);
typedef int (__stdcall *lpDiv)(int,int);
//上面的*lpPlus *lpSub *lpMul *lpDiv都是指針類型;
int main()
{
//在main函數裡面聲明函數指針變量;
lpPlus myPlus;
lpSub mySub;
lpMul myMul;
lpDiv myDiv;
//動态加載dll到記憶體中;
HINSTANCE hModule = LoadLibrary("dtljkcntf.dll");
//擷取函數位址;
myPlus = (lpPlus)GetProcAddress(hModule, "_Plus@8");
mySub = (lpSub)GetProcAddress(hModule, "_Sub@8");
myMul = (lpMul)GetProcAddress(hModule, "_Mul@8");
myDiv = (lpDiv)GetProcAddress(hModule, "_Div@8");
int x = myPlus(998,3);
printf("%d\r\n",x);
system("pause");
return 0;
}
特别說明:
Handle 是代表系統的核心對象,如檔案句柄,線程句柄,程序句柄。
HMODULE 是代表應用程式載入的子產品
HINSTANCE 在win32下與HMODULE是相同的東西 Win16 遺留
HWND 是視窗句柄
其實就是一個無符号整型,Windows之是以這樣設計有2個目的:
(1)可讀性更好
(2)避免在無意中進行運算
3.使用.def導出
下面兩步操作之前,先要建立一個動态連結庫的檔案模闆,這裡操作跟上面一樣,為這裡建立的名稱是dtljkdef;
然後生成下面.h和.cpp這兩個字尾的名字就是建立class操作即可;
(1)dtdef.h檔案
int Plus (int x,int y);
int Sub (int x,int y);
int Mul (int x,int y);
int Div (int x,int y);
(2)dtdef.cpp檔案
int Plus(int x,int y)
{
return x+y;
}
int Sub(int x,int y)
{
return x-y;
}
int Mul(int x,int y)
{
return x*y;
}
int Div(int x,int y)
{
return x/y;
}
(3)cntfdefs.def檔案 ---> 這一步操作就是建立一個文本檔案,我這的名稱是cntfdefs.def(New - Text File)寫入下面檔案;
需要注意:這裡建立這個文本的時候,要帶上後再名稱def,然後要勾選上面的添加到項目的複選框,否則編譯沒問題使用,有問題;
EXPORTS
Plus @12
Sub @15 NONAME
Mul @13 NONAME
Div @16
上述的NONAME就是隐藏名字的意思,為這裡隐藏了減法和乘法
(4)編譯;編譯完成之後複制dll檔案到需要使用此dll檔案的項目檔案夾裡面即可;
(4)使用序号導出的好處:
名字是一段程式就精華的注釋,通過名字可以直接猜測到函數的功能
通過使用序号,可以達到隐藏的目的.
動态連結庫dll測試效果
将生成的dll和lib檔案拷貝到建立的一個項目裡面
使用顯示連結的方式調用dll
dll内容
迷茫的人生,需要不斷努力,才能看清遠方模糊的志向!