用dll动态链接库将函数封装

时间:2021-10-02 15:48:59

如果熟悉windows的用户就会发现windows下有很多dll文件,但很多人不知道这些文件究竟是用来干什么的,为什么要做成dll文件。现在我就windows下的systems文件夹下的user32.dll文件,利用VC++6.0的depends工具打开,截图为:用dll动态链接库将函数封装

我们很容易发现,Function行都是我们开发用的API,也就是说微软通过将他们编写的函数放入一个dll文件中,而函数具体代码,我们是无法找到的,因为这是一个公司的核心竞争力所在,微软也不至于蠢到把windows开发代码给我们吧,那肯定是不现实的。

在这里我并不会对dll做详细的解析,我只想通过我的一个汉若塔程序,将我的程序进行封装,只提供相应的接口。

首先我们打开VC++6.0文件--新建--工程--Win32 Dynamic-Link Library

用dll动态链接库将函数封装确定默认一个空的工程。

然后新建一个头文件hrt_t.h

包含代码:

#define INIT_STACK_SIZE 40
#define STACK_ADD 10
typedef struct
{
 int xu;             //  对应的盘子序数
 int zbj;              //  对应的纵坐标
}cun;
typedef struct     //  把位子放入栈中 线性表操作
{
  cun *top;
  cun *bottom;
 int stacksize;
} Sqstack;
再新建一个头文件hrt.h

代码为:

extern "C" void __declspec(dllexport) initstack(Sqstack &L);
extern "C" Status __declspec(dllexport) push(Sqstack&L,int xu,int zbj);
extern "C" Status __declspec(dllexport) pop(Sqstack&L,int&xu,int&zbj);
extern "C" Status __declspec(dllexport) emptystack(Sqstack L);
extern "C" Status __declspec(dllexport) gettop(Sqstack L,int&xu,int&zbj);
extern "C" void __declspec(dllexport) GotoConsoleXY(HANDLE hConsole,int x,int y);
extern "C" void __declspec(dllexport) gotoxy(int x,int y);
extern "C" void __declspec(dllexport) huatu(Sqstack&A,Sqstack&B,char(*b)[16],char(*a)[16],int e,int zbj,int l,int xu);
extern "C" void __declspec(dllexport) move(Sqstack&A,Sqstack&B,Sqstack&C,char(*b)[16],char(*a)[16],char x,char Y);
extern "C" void __declspec(dllexport) hrt(Sqstack &e,Sqstack &f,Sqstack &g,char (*b)[16],char (*a)[16],int n,char A,char B,char C);

之后新建一个资源文件hrt.cpp

代码为:

#include"t11.h"
#include"hrt_t.h"
#include"hrt.h"
#include"stdio.h"
#include"malloc.h"
#include"stdlib.h"

void initstack(Sqstack &L)            //  初始化栈操作
{
 L.bottom=(cun*)malloc(INIT_STACK_SIZE*sizeof(cun));
 if(!L.bottom)
 {
  printf("内存分配失败!!");
  exit(-1);
 }
 L.top=L.bottom;
    L.stacksize=INIT_STACK_SIZE;
}

Status push(Sqstack &L,int xu,int zbj)        // 压入位置 q表示行,p表示列
{
 if(L.top-L.bottom >= L.stacksize)
 { L.bottom=(cun*)realloc(L.bottom,(L.stacksize+STACK_ADD)*sizeof(cun));
 if(!L.bottom)
 {
  printf("内存分配失败!!");
  exit(-1);
 }
 L.top=L.bottom+L.stacksize;
 L.stacksize+=STACK_ADD;
 }
 L.top->xu=xu;
 (L.top++)->zbj=zbj;
 
 
 return OK;
}

Status pop(Sqstack &L,int &xu,int&zbj)    //   弹出 将栈顶的元素带回
{
 if(L.top == L.bottom)
  return ERROR; 
 xu=(--L.top)->xu;            //  这点自己犯的问题  不能--L.top->i  这样就表示值减1 ,没意义,出错
 zbj=L.top->zbj;
 return OK;
}

Status emptystack(Sqstack L)         //  判断栈为空
{
 if(L.top == L.bottom)
  return OK;
 else
  return ERROR;
}

Status gettop(Sqstack L,int &xu,int &zbj)
{
 if(L.top == L.bottom)
  return ERROR;
 else
 {
  xu=(L.top-1)->xu;
  zbj=(L.top-1)->zbj;
  return OK;
 }
}

void  GotoConsoleXY(HANDLE  hConsole,int  x,int  y)  // 定位光标位置

 COORD  coordScreen={x,y}; 
 SetConsoleCursorPosition(hConsole,coordScreen); 
 return; 

void gotoxy(int  x,int  y)                        // 定位光标位置

 HANDLE  hStdOut=GetStdHandle(STD_OUTPUT_HANDLE); 
 GotoConsoleXY(hStdOut,x,y); 
 return; 
}

void huatu(Sqstack &A,Sqstack &B,char (*b)[16],char (*a)[16],int e,int zbj,int l,int xu)  // 画图
{
 int xs,hs; 
 if(e<l)   //  从左到右
 { 
  for(int i=0;i<zbj-5;i++)  // 从塔中出来到达顶端
  { 
   gotoxy(e-xu+1,zbj-i);         //  定位对应盘的起始盘子代号
   printf("%s",*(b+xu-1));      //  输出对应的空格字符代号
   gotoxy(e,zbj-i);           //  定位盘子的位置
   printf("|");                  // 输出竖杠
   gotoxy(e-xu+1,zbj-i-1);    //  定位产生动画的下一个光标位置
   printf("%s",*(a+xu-1));     //  打印出对应的盘子数
   Sleep(50);              // 暂停0.5毫秒
  }  
  for(i=0;i<l-e;i++)   //  平行移动
  {
   gotoxy(e-xu+1+i,5);         //  定位平行的位置
   printf("%s",*(b+xu-1));     //  将其对应的清除
   gotoxy(e-xu+2+i,5);         //  定位下一个单位
   printf("%s",*(a+xu-1));     //  打印出对应的盘子
   Sleep(20);                 //  暂停0.2毫秒
  }
  if(!emptystack(B))             //判断要移动到的盘子是否为空
   gettop(B,xs,hs);            //  得到该盘子的横行及序号
  else
   hs=14;                //  将横行置为最底层
  gotoxy(e-xu+1+i,5);       //  将要入盘的第一个位置定位
  printf("%s",*(b+xu-1));   //  输入对应空格覆盖
  gotoxy(e-xu+1+i,6);       //  定位入盘的光标位置
  printf("%s",*(a+xu-1));   //  空格覆盖
  Sleep(50);                //  暂停0.5毫秒
  for(int k=6;k<hs-1;k++)   //  进入要移动的指定塔上
  {
   gotoxy(e-xu+1+i,k);
   printf("%s",*(b+xu-1));
   gotoxy(l,k);
   printf("|");
   gotoxy(e-xu+1+i,k+1);
   printf("%s",*(a+xu-1));
   Sleep(50);
  }
  push(B,xu,k);      // 将序数和位置入栈
 }
 else             //  从右到左
 {    
  for(int i=0;i<zbj-5;i++)
  {
   gotoxy(e-xu+1,zbj-i);
   printf("%s",*(b+xu-1));
   gotoxy(e,zbj-i);
   printf("|");
   gotoxy(e-xu+1,zbj-i-1);
   printf("%s",*(a+xu-1));
   Sleep(50);
  }
  for(int k=e;k>=l;k--)
  {
   gotoxy(k-xu+1,5);
   printf("%s",*(b+xu-1));
   gotoxy(k-xu,5);
   printf("%s",*(a+xu-1));
   Sleep(20);   
  }
  if(!emptystack(B))
   gettop(B,xs,hs);
  else
   hs=14;
  gotoxy(l-xu,5);
  printf("%s",*(b+xu-1));
  gotoxy(l-xu+1,6);
  printf("%s",*(a+xu-1));
  Sleep(50);
  for( k=6;k<hs-1;k++)
  {
   gotoxy(l-xu+1,k);
   printf("%s",*(b+xu-1));
   gotoxy(l,k);
   printf("|");
   gotoxy(l-xu+1,k+1);
   printf("%s",*(a+xu-1));
   Sleep(50);
  }
  push(B,xu,k);   //  将盘子序数和要入的光标位置
 }
 Sleep(500);  //  暂停0.5秒 
}

void move(Sqstack &A,Sqstack &B,Sqstack &C,char (*b)[16],char (*a)[16],char X,char Y)
{
 int xu,zbj;
    switch(X)         //  匹配移动盘子代号
 {
 case 'A':
  switch(Y)
  {
  case 'B':{
   pop(A,xu,zbj);    //  弹出对应盘子的序数和横行位置
   huatu(A,B,b,a,16,zbj,40,xu); 
   p++;                //  全局变量加1,表示移动了一步
     };break;
  case 'C':{
   pop(A,xu,zbj);
   huatu(A,C,b,a,16,zbj,64,xu);
   p++;
     };break;
  };break;
  
  case 'B':
   switch(Y)
   {
   case 'A':{
    pop(B,xu,zbj);
    huatu(B,A,b,a,40,zbj,16,xu);
    p++;
      };break;
   case 'C':{
    pop(B,xu,zbj);
    huatu(B,C,b,a,40,zbj,64,xu);
    p++;
      };break;
   };break;
   case 'C':
    switch(Y)
    {
    case 'A':{
     pop(C,xu,zbj);
     huatu(C,A,b,a,64,zbj,16,xu);
     p++;
       };break;
    case 'B':{
     pop(C,xu,zbj);
     huatu(C,B,b,a,64,zbj,40,xu);
     p++;
       };break;
    }    
 }
}

void hrt(Sqstack &e,Sqstack &f,Sqstack &g,char (*b)[16],char (*a)[16],int n,char A,char B,char C)
{
 
 if(n == 1)
 { 
  gotoxy(28,3);
  printf("(第%d步,第%d个盘): %c->%c ",p,1,A,C);   //  动态显示移动盘子和步数
  move(e,f,g,b,a,A,C); 
 }
 else
    {
  hrt(e,f,g,b,a,n-1,A,C,B);           //  递归调用
  gotoxy(28,3);
  printf("                          ");
        gotoxy(28,3);
        printf("(第%d步,第%d个盘): %c->%c ",p,n,A,C);
  move(e,f,g,b,a,A,C);
  hrt(e,f,g,b,a,n-1,B,A,C);
    }
   
}

以上的步骤就是做一个dll库的过程,也就是将我自己编写的函数集成在dll文件里,进一步说,就是不想别人看见我编的代码,只是以借口的形式提供。全部重建后可以得到一个dll文件:

用dll动态链接库将函数封装

用depends工具打开:

用dll动态链接库将函数封装

可以发现列出的函数列表,就是我们上面的函数,dll文件只是以函数名的形式提供接口函数。也就是说,作为卖家,我可以将自己编写好的程序插件,以dll文件的形式的函数接口卖给买家。而买家完全不知道我的接口函数是怎样实现的。

接下来就是利用提供的接口函数,完成汉若塔程序。

首先我们得新建一个头文件t11.h这里包含一些基本的头文件

代码为:

#include"stdio.h"
#include"string.h"
#include"ctype.h"
#include"malloc.h"
#include"stdlib.h"  //atoi(),exit();
#include"io.h"      //eof()
#include"math.h"
#include"windows.h"


#define  TRUE  1
#define  FALSE  0
#define  OK   1
#define  ERROR 0

typedef int Status;
typedef int Boolean;

int p=1;

 

我们再新建一个资源文件main_hrt.cpp也就是主函数所在文件,这里有个小技巧,就是编写完头文件后,就全部重建,会在对应的文件夹下生成一个Debug文件,然后我们将刚才自己做的dll文件拷贝到Debug文件夹下,将函数的头文件hrt_t.h拷贝到工程文件夹下。这也就是我们发下,微软提供dll文件里面的函数,还会为我们提供头文件。比如说windows.h但我们绝对不会知道LoadLibrary()函数的实现代码。想必大家有点头绪了吧。 

用dll动态链接库将函数封装

代码为:

#include "windows.h"
#include"t11.h"
#include"hrt_t.h"
extern p;             //  引用全局变量
typedef void(*lpFun)(Sqstack&); //宏定义函数指针类型
typedef void(*lpXy)(int,int);
typedef Status(*lpPush)(Sqstack&L,int,int);
typedef void(*lpHrt)(Sqstack &e,Sqstack &f,Sqstack &g,char (*b)[16],char (*a)[16],int n,char A,char B,char C);
void main()
{
 Sqstack A,B,C;
 int n;
 HINSTANCE hDll = NULL;
 lpFun lp = NULL;
 lpXy lpxy = NULL;
 lpPush lppush = NULL;
 lpHrt lphrt = NULL;
 hDll = LoadLibrary("hrt_HH.dll");  // 默认exe当前文件夹中找
 if (hDll != NULL)
 {
        lp = (lpFun)GetProcAddress(hDll,"initstack");
  lpxy = (lpXy)GetProcAddress(hDll,"gotoxy");
        lppush = (lpPush)GetProcAddress(hDll,"push");
  lphrt = (lpHrt)GetProcAddress(hDll,"hrt");
        if (lp != NULL && lpxy!=NULL && lppush!=NULL &&lphrt!= NULL)
  {
            lp(A);
   lp(B);
   lp(C);
  }
  else
  {
   printf("链接库失败!");
    getchar();
   exit(0);
  }
    char a[][16]={"-","---","-----","-------","---------","-----------","-------------","---------------"};    // 定义盘子
 char b[][16]={" ","   ","     ","       ","         ","           ","             ","               "};    // 定义要覆盖对应的空格
 printf("输入盘子数(<=8):");
 scanf("%d",&n);
 printf("\n\n\n\n\n");
 if(n>8)
 {
  printf("输入不正确!");
         getchar();
    getchar();
  exit(1);
 }
 for(int i=1;i<=8;i++)
     printf("\t\t|\t\t\t|\t\t\t|\n");
     printf("\t\t A\t\t\t B\t\t\t C\n");

 for( i=0;i<n;i++)  //  将盘子的代号入栈
 {
  lpxy(17-n+i,13-i);
  printf("%s",a[n-1-i]);
  lppush(A,n-i,13-i);
 }
 printf("\n\n\n\n\n\n\n\n\n\n\n\n");
 getchar(); getchar(); 
    lphrt(A,B,C,b,a,n,'A','B','C');
 }
 else
 {
         printf("链接库失败!");
    getchar();
    getchar();
   exit(0);
  
 }
 printf("\n\n\n\n\n\n\n\n\n\t\t\t移动结束!~~~~~@@@@~~~~~\n\t\t\t历经步数:%d 步\n",p-1);
 getchar();
 FreeLibrary(hDll);
}

在工程文件中我们找不到程序的实现代码:

用dll动态链接库将函数封装

因为我们做成了dll文件,以这种形式提供。

文件下载地址:http://download.csdn.net/detail/wu10045125/4780820,如果把里面的dll文件拷走,程序就没有预期的结果。希望可以帮到大家,高手就飘过吧!