zoukankan      html  css  js  c++  java
  • DLL编程总结

      进行DLL的编程主要涉及到两个方面的问题,一个是要保证DLL中要导出的函数名不被编译器不可控地更改(在C++中由于重载机制的存在,会造成程序被编译时函数名被改变),其实就是要保证DLL导出的函数名与使用DLL时引用的函数名一致;另一个是怎样在应用程序中使用DLL的问题,实质就是怎样生成一个引用DLL的可执行程序的问题。解决这两个问题都有两种方法。下面就这两个问题进行详细讨论。

    一、DLL导出函数的两种方法

    DLL是包含若干个函数的库文件,其中包括内部函数和外部函数两种。前者用于DLL内部实现的调用,外部应用程序无法引用;后者则是提供给外部应用程序引用的函数,从而体现DLL的作用。应用程序在使用DLL中的函数之前,应该先导出欲发布的函数(即形成外部函数),这样才应用程序才能使用。要导出这些函数有两种方法,一是在定义函数时使用导出关键字__declspec(dllexport);另外一种方法是在创建DLL文件时使用模块定义文件.Def。下面通过两个例子来说明如何使用这两种方法创建DLL文件。

    方法1:使用导出函数关键字__declspec(dllexport)创建MyDll.dll,该动态链接库中有一个函数,实现对两个数做加法。在MyDll.h和MyDLL.cpp文件中代码如下:

    //MyDLL.h
    #ifndef MYDLL
    #define MYDLL extern "C" __declspec(dllimport)
    #endif
    MYDLL int Add(int a,int b);
    //MyDll.cpp
    #define MYDLL extern "C" __declspec(dllexport)
    
    #include"MyDll.h"
    int Add(int a, int b)
    {
    return a+b;
    } 

    该动态链接库编译成功后,在MyDll工程中的debug目录下,可以看到MyDll.dll、MyDll.lib两个文件。LIB文件中包含DLL文件名和DLL文件中的函数名等,该LIB文件只是对应该DLL文件的"映像文件",与DLL文件相比,LIB文件的长度要小的多,在采用隐式链接DLL时要用到它。对于MyDll.h中的extern"C"修改符。只有当你编写C++代码而不是直接编写C代码时,才能使用这个修改符。通常来说,C++编译器可能会改变函数和变量的名字,从而导致严重的连接程序问题。如果使用extern"C",就可以告诉编译器不要改变变量名或函数名,这样,变量和函数就可以供使用C、C++或其他任何编程语言编写的可执行模块来访问。

     

    方法2用.def文件创建工程MyDll。.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。使用.def文件创建DLL,就不需要再在上个例子创建的工程中的MyDll.h文件中包括extern"C"_declspec(dllexport)声明部分了。在该工程中加入一个文本文件,命名为MyDll.def,再在该文件中加入如下代码: 

    LIBRARY MyDll
    EXPORTS
    Add @1

    其中LIBRARY语句说明该def文件是属于相应DLL的,EXPORTS语句下列出要导出的函数名称。我们可以在.def文件中的导出函数后加@n,如Add@1,表示要导出的函数顺序号,在进行显式连时可以用到它。该DLL编译成功后,打开工程中的Debug目录,同样也会看到MyDll.dll和MyDll.lib文件。

     

    二、使用DLL的两种方法(DLL的链接)

    应用程序使用DLL可以采用两种方式:一种是隐式链接,另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。VisualC++6.0在VCin目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件中的函数结构。另外,Windows系统将遵循下面的搜索顺序来定位DLL:

    1.包含EXE文件的目录

    2.进程的当前工作目录

    3.Windows系统目录

    4.Windows目录

    5.列在Path环境变量中的一系列目录。

     

    方法1: 隐式链接

    隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。实现隐式链接很容易,简单地说,只要将导入函数关键字__declspec(dllimport)函数名写到应用程序相应的头文件中就可以了(如果DLL是按照上面的方式一导出函数,并且在头文件已经考虑了dllimport声明的情况,自然就不需要再在应用程序中加此关键字了)。具体操作步骤如下:

    (1)当创建可执行源代码文件时,必须加上DLL的头文件。如果没有头文件,输入的符号将不会被定义,这样编译器编译就会无法通过。其实这样做,编译器只是想确保你用正确的方法访问这些输入的符号。——DLL的头文件,供编译程序使用;

    (2)接下来,链接程序将所有的.obj模块组合起来,生成可执行模块。该链接程序必须确定哪些DLL包含代码所引用的所有输入符号。这由DLL相应的.lib提供。因此必须将DLL的.lib文件传递给链接程序。——DLL的.lib文件,供链接程序使用;

    下面的例子通过隐式链接调用MyDll.dll库中的Add函数。 

    //TestDll.cpp
    
    #include
    #include"MyDll.h"//上面定义的DLL对应的头文件
    #pragmacomment(lib,"MyDll.lib")  //保证应用程序在链接时能链接成功
    void main()
    {
    printf("%d
    ",Add(1,2));
    }

    在创建DllTest.exe文件之前,要先将MyDll.h和MyDll.lib拷贝到当前工程所在的目录下面,这样才能编译生成DllTest.ext;而在生成DllTest.exe文件后,无论是隐式还是显示链接,都只需将MyDll.dll文件拷贝到DllTest.exe所在目录,DllTest.exe就能加载到DLL文件,从而正常执行。当然,按照前面提到的应用程序搜索定位DLL的顺序,也可以将DLL放到其他目录如windows的System下。

    如果DLL使用的是def文件,要删除TestDll.h文件中关键字extern "C"。TestDll.h文件中的关键字Progamcommit是要Visual C+的编译器在link时,链接到MyDll.lib文件,当然,开发人员也可以不使用#pragmacomment(lib,"MyDll.lib")语句,而直接在工程的Setting->Link页的Object/Moduls栏加入MyDll.lib既可。

     

    方法2: 显式链接

    显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将需要的动态链接库调进来,此后再用GetProcAddress()获取想要引入的函数的地址。自此,你就可以像使用在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。下面是通过显式链接调用DLL中的Add函数的例子。 

    #include
    #include
    void main(void)
    {
          typedef int(*pAdd)(int a,int b);
          HINSTANCE hDLL;
          pAdd  Add;
          hDLL=LoadLibrary("add.dll");//加载动态链接库MyDll.dll文件;
          Add=(pAdd)GetProcAddress(hDLL,"add");
          printf("%d
    ",Add(1,2));
          FreeLibrary(hDLL);//卸载MyDll.dll文件;
    }  

    在上例中使用类型定义关键字typedef,定义指向和DLL中相同的函数原型指针,然后通过LoadLibray()将DLL加载到当前的应用程序中并返回当前DLL文件的句柄,然后通过GetProcAddress()函数获取导入到应用程序中的函数指针,函数调用完毕后,使用FreeLibrary()卸载DLL文件。同样,在运行程序时,需要先将DLL文件拷贝到系统能够搜到DLL文件的路径下。

    使用显式链接的应用程序在编译生成时不需要使用前面提到的任何Dlltest.h头文件以及Lib文件。另外,使用GetProcAddress()函数时,可以利用MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号,如将GetProcAddress(hDLL,"Add")改为GetProcAddress(hDLL,MAKEINTRESOURCE(1))(函数Add()在DLL中的顺序号是1),这样调用DLL中的函数速度很快,但是要记住函数的使用序号,否则会发生错误。

     

    三、一点补充说明 

    编译生成dll文件后,我们同时得到lib文件。这个lib和静态链接库的lib文件是完全不一样的:

    静态链接库的lib文件包含实现的二进制码,链接后就不要lib的支持了,lib中的代码会合并到exe文件中。  

    dll输出的lib,链接后还要dll的支持。因为它仅含导出函数的地址和一些位址信息,而不包括实现信息,这可以帮助Link程序完成连接(在此时安排调用入口地址及函数回调信息),调用模块可以模拟这个lib去修改相应的import项。这样,在运行时才能将DLL中真正的代码调入执行,实现动态连接。

     

     参考文献:Windows核心编程

  • 相关阅读:
    Map的两张遍历方法 keySet(),entrySet()
    http://localhost:8080请求用户名和密码。信息为:“XDB” 解决办法
    redis 集群出现的错误
    通信 缩略词
    redis
    hadoop 概念
    mysql 集群的一些概念
    SQL 基本关键字 函数 关联 连接
    JAVA集合
    SQL.Cookbook 读书笔记5 元数据查询
  • 原文地址:https://www.cnblogs.com/eaglediao/p/7136504.html
Copyright © 2011-2022 走看看