相关测试代码
代码复用的实现:
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
",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
",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
",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
",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内容