DLL的优点
*代码复用是提高软件开发效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架,ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。
“白盒复用”的缺点比较多,总结起来有4点。 暴露了源代码;多份拷贝,造成存储浪费; 容易与程序员的“普通”代码发生命名冲突; 更新功能模块比较困难,不利于问题的模块化实现; 实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。*
刚好需要创建一个DES加密解密的dll,所以下面DES加密解密程序作为示范
创建dll
创建动态链接库 (DLL) 项目
1 - 在菜单栏上,依次选择“文件”、“新建”、“项目”。
在“新建项目”对话框的左窗格中,依次展开“已安装”、“模板”、“Visual C++”,然后选择“Win32”。
在中间窗格中,选择“Win32 控制台应用程序”。
在“名称”框中为项目指定名称,例如testdll。 在“解决方案名称”框中为解决方案指定名称。 选择“确定”按钮。
在“Win32 应用程序向导”对话框的“概述”页上,选择“下一步”按钮。
在“应用程序设置”页面的“应用程序类型”下,选择“DLL”。
选择“完成”按钮创建项目。
2)创建头文件
在菜单栏上,依次选择“项目”、“添加新项”。 在“添加新项”对话框的左窗格中,在“Visual C++”下选择“代码”。 在中间窗格中,选择“头文件(.h)”。 为头文件指定名称(例如 testdll.h),然后选择“添加”按钮。 将显示一个空白头文件。
testdll.h
#ifndef TESTDLL_H_
#define TESTDLL_H_
#ifdef MYLIBDLL
#define MYLIBDLL extern "C" _declspec(dllimport)
#else
#define MYLIBDLL extern "C" _declspec(dllexport)
#endif
MYLIBDLL void Des(char *M,char *Out , char *key,bool En_Deflag);
// TestDll.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "testdll.h"
#include "Box.h"
#include <iostream>
using namespace std;
void Convert(bool *In,bool *Out, const int *T,int num);
void F_des(bool R[32],bool const *Subkey );
void Byte2Bit(bool *Out, const int *In, int bits);//字节组转换成位组
void RotateL(bool *In, int len, int loop);//循环左移
void Xor(bool *InA, const bool *InB, int len);//异或
void SboxConvert(bool *out,const bool In[48]);//S盒变换
bool S_Key[16][48];// 16个子密钥
//字符转成二进制,length字符数组的长度
void Char2Bit(const char* _carray, bool* _barray, int length)
{
for (int i = 0; i <length; i++)
{
for (int j = 0; j < 8; j++)
{
_barray[i * 8+ 7 - j] = (_carray[i] >> j) & 1;
}
}
};
//二进制转成字符
const char bitmask[8] = { 128, 64, 32, 16, 8, 4, 2, 1 };
void Bit2Char(bool* _barray, char* _carray)
{
char temp;
for (int i = 0; i < 8; i++)
{
//数学方法转成字符
temp = 0;
for (int j = 0; j < 8; j++)
{
if (_barray[i * 8 + j] == 1)
{
temp |= bitmask[j];
}
}
_carray[i] = temp;
}
};
void Byte2Bit(bool *Out, const char *In, int bits)//字节组转换成位组
{
for(int i = 0; i < bits; i++)
{
Out[i] = (In[i/8] >> (i%8)) & 1;
}
}
void Bit2Byte(char *Out, const bool *In, int bits)
{
memset(Out, 0, (bits+7)/8);
for(int i = 0; i < bits; i++)
{
Out[i/8] |= In[i] << (i%8);
}
}
//初始化得到密匙流
void Get_key(const char key[])
{
static bool K[64];
bool C0[28],D0[28];
Char2Bit(key,K ,8);
Convert(K,C0,c0,28);
Convert(K,D0,d0,28);
memcpy(K,C0,28);
memcpy(&K[28],D0,28);
for(int i=0;i<16;i++)
{
RotateL(K,28,keyoff[i]);
RotateL(&K[28],28,keyoff[i]);
Convert(K,S_Key[i],di,48);//变换
//Convert(C0,*(S_Key+i),di,24);//*(a+i)=a[i]
//Convert(D0,(*(S_Key+i)+24),&di[24],24);//C0 D0经过每次迭代之后都不同
//注意这边D0要从$di[24]开始取相应位数的值
}
}
//En_Deflag=1是加密,0是解密
void Des(char *M,char *Out , char *key,bool En_Deflag)
{
Get_key(key);
static bool Ip[64],Temp[32];
bool *Li=&Ip[0],*Ri=&Ip[32];
Char2Bit(M,Ip, 8);
/*cout<<"明文转成64位是:"<<endl;
for(int i=0;i<64;i++){
cout <<Ip[i]<<' ';
}*/
Convert(Ip,Ip,ip,64); //对64位的明文进行初始化置换IP
if(En_Deflag==1)
{
for(int i=0;i<=15;i++)
{
memcpy(Temp, Ri, 32);
F_des(Ri,S_Key[i]);
Xor(Ri,Li,32);
memcpy(Li,Temp,32);
}
//R16L16变成L16R16
/* memcpy(Temp, Ri, 32);
memcpy(Ri,Li,32);
memcpy(Li,Temp,32);*/
}
else
{
for(int i=15;i>=0;i--)
{
memcpy(Temp, Li, 32);
F_des(Li,S_Key[i]);
Xor(Li,Ri,32);
memcpy(Ri,Temp,32);
}
//R16L16变成L16R16
/* memcpy(Temp, Ri, 32);
memcpy(Ri,Li,32);
memcpy(Li,Temp,32);*/
}
Convert(Ip,Ip,back_ip,64);
Bit2Char(Ip, Out);
// Bit2Byte(Out,Ip,64);
}
//f函数
void F_des(bool R[32],bool const *Subkey )
{
bool ER0[48];
Convert(R,ER0,e_operate ,48);//R扩展成48为
Xor(ER0,Subkey,48);//扩展后的与ki进行异或
SboxConvert(R,ER0);//s盒变换
Convert(R, R,p_operate,32);//对8个S盒的值进行置换运算P操作
}
//变换
void Convert(bool *In,bool *Out, const int *T,int num)
{
static bool Tmp[256];
for(int i = 0; i < num; i++)
{
Tmp[i] = In[ T[i] - 1 ];//取In数组中第i个
}
memcpy(Out, Tmp, num);
}
//s盒变换,第一六位对应的十进制数作为行,第二三四五为的对应十进制数作为列, 48位转成32位
void SboxConvert(bool out[32],const bool In[48])
{
int i,ao,bi;
int x,y;
char tmp;
for(i=0;i<8;i++)
{
bi=i*6;
x=(In[bi]<<1) +In[bi+5];
y=(In[bi+1]<<3)+(In[bi+2]<<2)+(In[bi+3]<<1)+In[bi+4];
tmp=sbox[i][x][y];
ao=i*4;
for(int j=0;j<4;j++)
out[ao+(3-j)]=(tmp>>j)&1;
//Byte2Bit(out, &sbox[i][16*x+y], 4); //这边取sbox里面的第i个盒子里面的x行y列 ByteToBit(out, &sbox[i][4*x+y], 4); &sbox[i][x][y]
}
}
void Xor(bool *InA, const bool *InB, int len)//异或
{
for(int i = 0; i < len; i++)
{
InA[i] ^= InB[i];
}
}
void RotateL(bool *In, int len, int loop)//循环左移
{
static bool Tmp[256];
memcpy(Tmp, In, loop);
memcpy(In, In+loop, len-loop);
memcpy(In+len-loop, Tmp, loop);
}
模块定义 (.def) 文件为链接器提供有关被链接程序的导出、属性及其他方面的信息。生成 DLL 时,.def 文件最有用。由于存在可代替模块定义语句使用的链接器选项,通常不需要 .def 文件。也可以将 __declspec(dllexport) 用作指定导出函数的手段。在链接器阶段可以使用 /DEF(指定模块定义文件)链接器选项调用 .def 文件。如果生成的 .exe 文件没有导出,使用 .def 文件将使输出文件较大并降低加载速度。
在VC++中,生成DLL可以不使用.def文件。只需要在VC++的函数定义前要加__declspec(dllexport)修饰就可以了。但是使用__declspec(dllexport)和使用.def文件是有区别的。如果DLL是提供给VC++用户使用的,你只需要把编译DLL时产生的.lib提供给用户,它可以很轻松地调用你的DLL。但是如果你的DLL是供其他程序如VB、delphi,以及.NET用户使用的,那么会产生一个小麻烦。
因为VC++对于__declspec(dllexport)声明的函数会对函数名尾附加函数在DLL中的地址,如下面的函数:
__declspec(dllexport) int __stdcallIsWinNT()
会转换为IsWinNT@0,这样你在VB中必须这样声明:
Declare Function IsWinNT Lib “my.dll” Alias “IsWinNT@0” () As Long
@的后面的数由于参数类型不同而可能不同。这显然不太方便。所以如果要想避免这种转换,就要使用.def文件方式。
EXPORTS后面的数可以不给,系统会自动分配一个数。对于VB、PB、Delphi用户,通常使用按名称进行调用的方式,这个数关系不大,但是对于使用.lib链接的VC程序来说,不是按名称进行调用,而是按照这个数进行调用的,所以最好给出。—引自百度
3新建模块定义文件source.def//这边使用系统默认的文件名
盗了个图,意思懂就行
source.def
LIBRARY TestDll
EXPORTS
Des @1
4编译生成dll
5 新建一个win32控制台应用程序,把生成的testdll.dll拷贝到新项目的debug文件夹里面。
6应用程序使用DLL可以采用两种方式:一种是隐式链接(调用),另一种是显式链接。
隐式链接采用静态加载的方式,比较简单,需要.h、.lib、.dll三件套。。
新建“控制台应用程序”或“空项目”配置如下:(非常关键)
项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件testdll.h所在的目录
项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件testdll.lib所在的目录
项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“testdll.lib”(若有多个 lib 则以空格隔开)
添加cpp文件
#include "stdafx.h"
#include <iostream>
#include <string>
#include "testdll.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
//char key[8]={1,2,3,4,5,6,7,8};
char key[8]={ '1','2','3','4','5','6','7','8' };
char msg[8] = { '0','1','2','3','4','5','6','7'};
char Out1[8];
char Out2[8];
cout<<"明文是:"<<endl;
for(int i=0;i<8;i++)
cout<<msg[i];
cout<<endl;
cout<<"现在加密:"<<endl;
Des(msg, Out1,key,1);
cout<<"密文是:"<<endl;
for(int i=0;i<8;i++)
cout<<Out1[i];
cout<<endl;
cout<<"现在解密:"<<endl;
Des(Out1, Out2,key,0);
cout<<"明文是:";
for(int i=0;i<8;i++)
cout<<Out2[i];
cout<<endl;
system("pause");
return 0;
}
测试结果