如果熟悉windows的用户就会发现windows下有很多dll文件,但很多人不知道这些文件究竟是用来干什么的,为什么要做成dll文件。现在我就windows下的systems文件夹下的user32.dll文件,利用VC++6.0的depends工具打开,截图为:
我们很容易发现,Function行都是我们开发用的API,也就是说微软通过将他们编写的函数放入一个dll文件中,而函数具体代码,我们是无法找到的,因为这是一个公司的核心竞争力所在,微软也不至于蠢到把windows开发代码给我们吧,那肯定是不现实的。
在这里我并不会对dll做详细的解析,我只想通过我的一个汉若塔程序,将我的程序进行封装,只提供相应的接口。
首先我们打开VC++6.0文件--新建--工程--Win32 Dynamic-Link Library
然后新建一个头文件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文件:
用depends工具打开:
可以发现列出的函数列表,就是我们上面的函数,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()函数的实现代码。想必大家有点头绪了吧。
代码为:
#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文件,以这种形式提供。
文件下载地址:http://download.csdn.net/detail/wu10045125/4780820,如果把里面的dll文件拷走,程序就没有预期的结果。希望可以帮到大家,高手就飘过吧!