zoukankan      html  css  js  c++  java
  • DLL的创建与使用

    一、动态链接库(DLL)

    动态链接库提供了一种方法,使进程可以调用不属于其执行代码的函数。函数的可执行代码位于一个.dll文件中,该文件包含一个或多个已被编译、链接并使用它们的进程分开存储的函数。

    优点:

    1.扩展了 应用程序的特性

    2.可以使用多种编程语言来编写

    3.简化项目的管理

    二、依赖项

    当某个程序或DLL使用其他DLL或DLL函数时,就会创建依赖项,因此程序就不会再独立了,依赖项如果被破坏,该程序可能出现问题。

    三、入口点

    在创建DLL时,可以有选择地指定入口点的函数。当进程或线程将它们自身附加到DLL或者将它们自身以DLL分离时,都会调用入口函数。入口函数应该只进行简单的初始化工作,而不应该调用其他DLL函数或者终止函数。

    关于创建DLL,我的环境是VS2017,步骤为:文件->新建项目->选择动态链接库(DLL)->完成。在创建的项目中的源文件可以看到有一个已经创建好的dllmain.cpp,这个就DLL入口点的代码了。

     1 // dllmain.cpp : 定义 DLL 应用程序的入口点。
     2 #include "stdafx.h"
     3 BOOL APIENTRY DllMain( HMODULE hModule,     //模块句柄
     4                        DWORD  ul_reason_for_call,//调用原因
     5                        LPVOID lpReserved   //参数保留,好像没什么用
     6                      )
     7 {
     8     switch (ul_reason_for_call)
     9     {
    10     case DLL_PROCESS_ATTACH:  //DLL被某个程序加载
    11     case DLL_THREAD_ATTACH:   //DLL被某个线程加载
    12     case DLL_THREAD_DETACH:   //DLL被某个线程卸载
    13     case DLL_PROCESS_DETACH:  //DLL被某个程序加载
    14         break;
    15     }
    16     return TRUE;
    17 }

    四、如何导出(在DLL中实现的)

    第一种:

    步骤1:向所有需要导出的DLL函数中添加关键字__declspec(dllexport)。

    步骤2:要在应用程序中使用导出的DLL函数,必须使用__declspec(dllexport)来声明要导入的各个函数。

    本章使用的就是这种方法

    通常最好使用一个包含define语句和ifdef语句的头文件,以便分隔导出语句和导入语句,代码如下。

    说明:该DLL库实现的是简单的加减乘除运算,注意实现减法的这个函数,我这里特别使用了一个抽象类来实现,我发现有些企业代码都会有这种类似的抽象类,然后用纯虚函数做接口。

     1 //__declspec(dllexport)修饰符指示编译器和链接器从 
     2 //DLL 导出函数或变量,以便其他应用程序可以使用它,在ExprotDll.h中
     3 #pragma once
     4 #ifdef EXPORTDLL_EXPORTS
     5 #define EXPORTDLL_API __declspec(dllexport)
     6 #else
     7 #define EXPORTDLL_API __declspec(dllimport)
     8 #endif
     9 
    10 //导出的类
    11 class EXPORTDLL_API CExportDll {
    12 public:
    13     // TODO:  在此添加方法
    14     //用纯虚函数做为接口
    15     virtual int SUBTARCT(int a, int b) = 0;
    16     virtual ~CExportDll() {};
    17 
    18 };
    19 
    20 //导出函数,如果是在C++环境下,则执行"{" "}"中的内容
    21 #ifdef __cplusplus
    22 extern "C" {
    23 #endif
    24     EXPORTDLL_API CExportDll* Create();
    25     EXPORTDLL_API void Destroy(CExportDll* ex);
    26 #ifdef __cplusplus
    27 }
    28 #endif
    29 
    30 //也可用下列方法定义导出函数
    31 extern "C" EXPORTDLL_API int nExportDll;
    32 extern "C" EXPORTDLL_API int ADD(int a, int b);
    33 extern "C" EXPORTDLL_API int MULTIPLY(int a, int b);
    34 extern "C" EXPORTDLL_API float DIVIDE(float a, float b);

    第二种:

    创建模块定义文件.def以列出导出的DLL函数:

    五、特别调用

    需要注意以下一些情况:

    1.如果使用了Win32 API,则应该使用关键字__stdcall修饰函数

    2.如果将C++生成的DLL供标准C语言使用,输出文件用extern "C"来修饰,设置导出函数时采用.def文件形式,而不是__declspec(dllexport)

    六、编写DLL

    创建DLL工程并且定义了相应的导出函数、变量或类之后,接下来就是实现功能了。本章中因为CExportDll是一个抽象类,所以需要再创建一个它的子类(CExportDllChild.h中),实现相应的接口功能(EXportDll.cpp中),同时其他函数也在(EXportDll.cpp)中实现功能。

    1 //ExportDllChild.h
    2 #pragma once
    3 #include "ExportDll.h"
    4 
    5 class CExportDllChild :public CExportDll
    6 {
    7     virtual int SUBTARCT(int a, int b);
    8     virtual ~CExportDllChild() {};
    9 };
     1 // ExportDll.cpp : 定义 DLL 应用程序的导出函数。
     2 
     3 #include "stdafx.h"
     4 #include "ExportDllChild.h"
     5 
     6 //这是一个导出变量的一个示例
     7 EXPORTDLL_API int nExportDll = 0;
     8 
     9 //这是导出函数的几个示例
    10 //当前使用的是C++编译出来的
    11 
    12 //构造对象
    13 EXPORTDLL_API CExportDll* Create()
    14 {
    15     return new CExportDllChild;
    16 }
    17 //析构对象
    18 EXPORTDLL_API void Destroy(CExportDll* ex)
    19 {
    20     delete ex;
    21 }
    22 //
    23 EXPORTDLL_API int ADD(int a, int b)
    24 {
    25     return a + b;
    26 }
    27 //
    28 int CExportDllChild::SUBTARCT(int a, int b)
    29 {
    30     return a - b;
    31 }
    32 //
    33 EXPORTDLL_API int MULTIPLY(int a, int b)
    34 {
    35     return a * b;
    36 }
    37 //
    38 EXPORTDLL_API float DIVIDE(float a, float b)
    39 {
    40     if (b != 0) {
    41         return a / b;
    42     }
    43     else
    44         return -1;
    45 }

    创建编写好后,进行生成,可以在项目Debugp目录下看到生成的动态库.dll和静态库.lib

    七、调用DLL

    调用DLL有两种方法,一种是显式链接方式,另一种是隐式链接方式。

    在调用前,先创建一个Win32控制台应用程序或者MFC,这里创建的是一个简单的Win32程序(LoadDll)

    1.显式链接:

    在此项目中,需要将CExportDllChild.h放在LoadDll目录下。然后在cpp中添加以下代码

     1 // LoadDll.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
     2 //
     3 
     4 #include "pch.h"
     5 #include <Windows.h>
     6 #include <iostream>
     7 #include "ExportDll.h"
     8 
     9 /*-------------动态链接-------------*/
    10 int main()
    11 {
    12     HMODULE hModule;   //模块句柄
    13     //定义函数指针
    14     typedef int(*FUNC1)(int a, int b);
    15     typedef CExportDll* (*CreateFun2)();
    16     typedef void(*DestroyFun2)(CExportDll* ex);
    17     typedef int(*FUNC3)(int a, int b);
    18     typedef float(*FUNC4)(float a, float b);
    19     //调用DLL,先找当前文件夹,如果没有,就会去system32下查找 
    20     hModule = ::LoadLibrary(L"ExportDll.dll"); 
    21     
    22     if (hModule == NULL){
    23         MessageBox(NULL, L"DLL加载失败", L"Mark", MB_OK);
    24     }
    25     //获取相应DLL函数的入口地址
    26     FUNC1 add = (FUNC1)::GetProcAddress(hModule, "ADD");
    27     CreateFun2 create = (CreateFun2)::GetProcAddress(hModule, "Create");
    28     DestroyFun2 destroy = (DestroyFun2)::GetProcAddress(hModule, "Destroy");
    29     FUNC3 multiply = (FUNC3)::GetProcAddress(hModule, "MULTIPLY");
    30     FUNC4 divide = (FUNC4)::GetProcAddress(hModule, "DIVIDE");
    31     //
    32     if (add != NULL){
    33         std::cout << "a+b="<< add(10, 5) << std::endl;
    34     }
    35     //
    36     if (create&&destroy) 
    37     {
    38         CExportDll* p = create();
    39         std::cout << "a-b=" << p->SUBTARCT(10, 5) << std::endl;
    40         destroy(p);
    41         p = NULL;
    42     }
    43     //
    44     if (multiply != NULL){
    45         std::cout << "a*b=" << multiply(10, 5) << std::endl;
    46     }
    47     //
    48     if (divide != NULL)
    49     {
    50         std::cout << "a/b=" << divide(10, 5) << std::endl;
    51     }
    52     FreeLibrary(hModule);   //释放句柄
    53     return 0;
    54 }

    调用结果:

    2.隐式调用

    在程序开始执行时就将DLL文件加载到应用程序中。隐式调用没用过,不做表述了。

  • 相关阅读:
    B/S与C/S的联系与区别
    ASP.NET中常用的26个优化性能方法(二)
    ASP.NET下如何防范SQL注入式攻击
    ASP.NET中常用的26个优化性能方法(一)
    Invoke and BeginInvoke BeginInvoke和EndInvoke方法 (转)
    C#中海量数据的批量插入和更新 [转]
    [译文]从底层角度看ASP.NETA lowlevel Look at the ASP.NET Architecture( 转)
    C#制作Windows service服务系列
    通过C++ Interop把Windows窗体集成到MFC应用程序中
    【转】.NET内存管理、垃圾回收
  • 原文地址:https://www.cnblogs.com/main404/p/11185127.html
Copyright © 2011-2022 走看看