在C++中调用DLL中的函数

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;
}

测试结果
这里写图片描述

发布了7 篇原创文章 · 获赞 6 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ermeiyao11/article/details/68948865