zoukankan      html  css  js  c++  java
  • 动态链接库(DLL)

           DLL(Dynamic Link Library)也就是动态链接库,是一个可以被其他应用程序调用的程序模块,其中封装了可以被调用的资源或函数。DLL文件属于可执行文件,它符合Windows系统的PE文件格式,它依附于EXE文件创建的进程来执行,不能单独运行。一个DLL文件可以被多个进程所装载调用。

    一、DllMain( )函数

           DLL程序的入口函数为DllMain( )函数。使用VC++创建DLL程序,系统默认会生成如下代码

     1 BOOL APIENTRY DllMain( HMODULE hModule,//指向DLL本身的实例句柄;
     2                        DWORD  ul_reason_for_call,//指明了DLL被调用的原因;
     3                        LPVOID lpReserved   //保留值,暂时没有意义;
     4                      )
     5 {
     6     switch (ul_reason_for_call)
     7     {
     8     case DLL_PROCESS_ATTACH:
     9     case DLL_THREAD_ATTACH:
    10     case DLL_THREAD_DETACH:
    11     case DLL_PROCESS_DETACH:
    12         break;
    13     }
    14     return TRUE;
    15 }

           但在使用过程中,DllMain( )函数不是必须由我们提供、定义的, 这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本。

           根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。DllMain( )函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,它是自动被调用的。不要将DllMain写成DLLMain

    二、DLL函数的导出

    方式1:关键词_declspec(dllexport) 导出方式

           首先添加一个用于导出函数的头文件,在头文件中放置需要导出的函数声明。

           在使用C语言编写DLL函数时,在函数的声明前加上_declspec(dllexport)  链接器便会在生成的DLL文件中嵌入一个导出符号表。使用Depends工具可以查看到。使用C++编写DLL时,则要在函数声明前加上extern "C" _declspec(dllexport),可避免编译器对函数名进行改编。

           注意!当C/C++编写的函数使用了__stdcall也就是WINAPI调用约定时,即使使用的是extern "C" _declspec(dllexport),Microsoft的编译器还是会对函数名进行改编。改编的方法是给函数名添加下划线前缀和一个特殊的后缀,该后缀由一个@符号后跟作为参数传给函数的字节数组成。

    extern "C" _declspec(dllexport) LONG __stdcall MyFunc(int a, int b);//该函数在DLL的导出段中被导出为_MyFunc@8。

    如果想要导出未经改编的函数名,可在DLL的源文件中加入如下代码:

    #pragma comment(linker, "/export:MyFunc=_MyFunc@8")

    使用这种方法时,DLL实际上导出了两个函数MyFunc和_MyFunc@8 ,而使用下面介绍的Def文件导出方法则不会存在这个问题。 

    方式2:Def文件导出

    ■文件中的第一个语句必须是 LIBRARY 语句。此语句将 .def 文件标识为属于 DLL。LIBRARY 语句的后面是 DLL 的名称。链接器将此名称放到 DLL 的导入库中。

    ■EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。

    ■注释语句,在语句前面加分号 “;” 。

    例如:

    ;DLLTest.def : Declares the module parameters for the DLL.
    LIBRARY   "DLLTest"
    EXPORTS
       add   @1
       fun   @2

    三、对DLL程序的调用

           对DLL的调用分为两种,静态调用和动态调用。

           我们可以首先创建一个名为MyDLL的DLL工程,其中添加DLLShow函数。

    1 #include <Windows.h>
    2 extern "C" _declspec(dllexport) void MyDLL(char* szMessage);
    3  
    4 void MyDLL(char* szMessage)
    5 {
    6     MessageBox(NULL,szMessage,"MyDLL",MB_OK);
    7 }

           编译生成的MyDLL.dll和MyDLL.lib两个文件将在调用中使用到。

           调用方法一:静态调用。

           创建一个调用DLL文件的EXE程序,命名为CallDLL。如下:

    #include<Windows.h>
    #pragma comment(lib,"MyDLL")
     
    extern "C" void DLLShow(char* szMessage);
     
    int _stdcall WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, __in int nShowCmd )
    {
         DLLShow("HelloWorld!");
         return 0;
    }

           直接编译的话,会产生一个连接错误。无法打开文件“MyDLL.lib”,需要将之前编译好的MyDLL.lib和MyDLL.dll文件添加到工程目录下。

           注意,是工程目录下(有.cpp文件的目录),添加到其它目录下仍旧会提示这个错误。

           静态调用是通过连接器将DLL的导出函数写进可执行文件中。下面提到的动态调用则是在程序运行时完成调用的。

           调用方法二:动态调用。

           首先需要知道DLL中函数的声明。然后通过以下几个步骤进行动态的加载。

           1、根据函数声明构建函数指针。

           2、使用LoadLibrary函数加载dll文件。

           3、使用GetProcAddress将DLL中的函数指针赋值给我们构建的函数指针。

           到此,函数指针就具有了DLL函数一样的功能。

     1 #include<Windows.h>
     2 typedef void (*PFUN)(char*);
     3 int _stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd )
     4 {
     5      HMODULE hModule =LoadLibrary("Test.dll");
     6      if (hModule==NULL)
     7      {
     8           MessageBox(NULL,"MyDLL文件不存在","加载失败",MB_OK);
     9           return -1;
    10      }
    11      PFUN pFun=(PFUN)GetProcAddress(hModule,"DLLShow"); 
    12      pFun("Hello World!");
    13      return 0;
    14 }        

           相比两种调用方式,动态调用有很多的优点。比如有些动态链接库没有提供lib文件,也有时候我们可以能要编写DLL文件供别人使用、或自己在其他语言中调用,等等很多情况下,我们只有动态调用一种方式。

    四、卸载DLL模块

          当进程不再需要引用DLL中的符号时,我们应该将其从进程的地址空间中卸载。

          如果DLL没有创建线程,我们使用FreeLibrary函数

    BOOL  FreeLibrary(HMODULE hInstDll);

          我们需要传入标识DLL的句柄,也就是我们使用调用的LoadLibrary函数的返回值。

          如果DLL创建了其他线程,我们需要使用FreeLibraryAndExitThread函数。

    VOID FreeLibraryAndExitThread(HMODULE  hInstDll,  DWORD  dwExitCode);
  • 相关阅读:
    单元测试,集成测试与系统测试
    关于 单窗口服务模型模拟 进行的小测试
    软件测试新随笔
    白盒测试
    黑盒测试小实验
    JUnit框架初次体验
    等价类划分进阶篇
    等价类划分
    因果图法测试小例
    android中将EditText改成不可编辑的状态
  • 原文地址:https://www.cnblogs.com/cnstalker/p/4829159.html
Copyright © 2011-2022 走看看