动态链接库的创建和调用方法


    最近在写dll,看了一些网上的资源,自己整理了一下,这里把它贴出来。好了,进入正题。

    我今天要讲的主要是非mfcdll,也就是win32 dynamic link library ,如下图所示:

                                               

  图   1

1. 一个简单的dll示例

            如图1所示,在file菜单下new一个个Win32 Dynamic-Link Library工程,取名为simple_dll。在建立的工程中添加如下文件

/* 文件名:simple_dll.h */
#ifndef SIMPLE_DLL_H
#define SIMPLE_DLL_H
extern "C" int _declspec(dllexport)add(int x, int y);
#endif


/* 文件名:simple_dll.cpp */

#include "simple_dll.h"

int add(int x, int y)
{
      return x + y;
}
如上就建立了一个简单的动态链接库工程。按下F7,或者点击build菜单下的build simple_dll.dll可以在debug文件夹下生成simple_dll.lib和simple_dll.dll文件。

接下来我们编写一个win32 console application工程调用dll中的add函数。在文件菜单下点击新建,new一个win32 console application工程,如下图所示。  名称设为call_simple_dll。
                                               

然后把simple_dll工程中的simple_dll.lib和simple_dll.dll拷贝到call_simple_dll工程目下文件夹下。最后在call_simple_dll下新建main.cpp文件。

/**********文件main.cpp**************/
#pragma comment(lib,"simple_dll.lib")
#include<stdio.h> 
//.lib 文件中仅仅是关于其对应DLL 文件中函数的重定位信息
extern "C" _declspec(dllimport) add(int x,int y);

int main(int argc, char* argv[])
{
    int result = add(2,3);
    printf("%d",result);
    return 0;
}
调用成功,结果显示为5.

2.  调用dll中的函数

dll中的函数有两种调用方式,静态的和动态的。第1节中的例子属于静态调用。动态调用的方式如下:

按照第1节中的方式建立好工程,及main.cpp。具体代码改成如下:

#include <stdio.h>
#include <windows.h>
typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
int main(int argc, char *argv[])
{
    HINSTANCE hDll; //DLL 句柄
    lpAddFun addFun; //函数指针
    hDll = LoadLibrary("..\\Debug\\dllTest.dll");
    if (hDll != NULL)
   {
      addFun = (lpAddFun)GetProcAddress(hDll, "add");
      if (addFun != NULL)
      {
         int result = addFun(2, 3);
         printf("%d", result);
      }
      FreeLibrary(hDll);
   }
   return 0;
}
编译运行,可以得到和第1节中同样的结果。这里我们可以看到所谓动态调用就是使用了LoadLibrary——GetProcAddress——FreeLibrary三个windows API函数。静态调用和动态调用的区别在于,静态调用需要包含lib描述文件。客户端(即main函数)编译连接的时候,会把dll中该函数的地址绑定到main函数。这样,一旦dll发生改变,那么函数的地址也会发生改变。客户端也必须重新编译连接。而动态调用方式,因为使用后期动态绑定,连接的时候不需要知道函数的具体地址。只有在调用的时候,才进行连接。这样,无论dll中的函数怎么修改,反正在客户端调用时会对该函数的地址进行计算,所以即使dll发生改变,也不需要重新编译客户端程序。这就是动态调用和静态调用最主要的区别。当然大家也可以看到,静态调用代码要简洁一些。所以,如果dll功能固定的话,大家可以采用静态链接的方式。

3. dll中函数的导出

可以看到第1节中我们使用

extern "C" int _declspec(dllexport)add(int x, int y);
来声明add函数,这里_declspec(dllexport)的意思就是声明说明(declaration spec)add函数是一个dll导出函数。我们可以把simple_dll.dll用vc6 tools自带的depends工具查看,  如下图,我们可以看到add函数的入口指针为0x00001005 。这样说明将add符号才可以被外界调用。
                                                     
4.dll中变量的导出

变量的导出方式跟函数基本一样

/*文件名: variable.h */
#ifndef _VARIABLE_H_
#define _VARIABLE_H_

#ifdef EXPORT
#define VARIABLE_DLL _declspec(dllexport)
#else
#define VARIABLE_DLL _declspec(dllimport)
#endif
extern VARIABLE_DLL int dllglobal;
#endif


/*文件名:variable.cpp */
#define EXPORT
#include "variable.h"

int dllglobal = 33;


在主函数中引用DLL 中定义的全局变量:

/* 文件名:main.cpp */

#include "variable.h"
#include<stdio.h>
#pragma comment(lib,"variable.lib")

int main()
{
    printf("%d",dllglobal);
}  

注意,这里需要将variable.h,variable.lib,variable.dll都拷贝到main.cpp同个文件夹下。否则编译不会成功。


5.dll中类的导出

下面的例子里,我们在DLL 中定义了point 和circle 两个类,并在应用工程中引用了它们。

//文件名:point.h,point 类的声明
#ifndef POINT_H
#define POINT_H

#ifdef   EXPORT
#define CLASS_DLL _declspec(dllexport)
#else
#define CLASS_DLL _declspec(dllimport)

#endif

class CLASS_DLL point //导入类point{public:float y;float x;point();point(float x_coordinate, float y_coordinate);};

#endif


//文件名:point.cpp,point 类的实现
#define CLASS_DLL
#include "point.h"
//类point 的缺省构造函数
point::point()
{
   x = 0.0;
   y = 0.0
}
//类point 的构造函数
point::point(float x_coordinate, float y_coordinate)
{
   x = x_coordinate;
   y = y_coordinate;
}

客户端程序
#include "circle.h" //包含类声明头文件
#pragma comment(lib,"circle.lib");
int main()
{
    circle c;
    printf("area:%f girth:%f", c.x, c.y);
    return 0;
}




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

猜你喜欢

转载自blog.csdn.net/warlice/article/details/8678853