C语言dll文件的说明以及生成、使用方法

 andlly   2018-07-08 15:46   199 人阅读  0 条评论

        DLL文件就是动态链接库,也是Windows的基石。一般用C语言(C/C++/C#)创建dll文件。动态链接库(Dynamic Link Library )是一种不可执行的二进制程序文件它允许多个程序共享执行特殊任务所必需的代码和其他资源。Windows 中,DLL 多数情况下是带有 ".dll" 扩展名的文件,但也可能是 ".ocx"或其他扩展名;Linux系统中常常是 ".so" 的文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。使用动态链接库可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。是开发大型项目必不可少的部分。


 优点:(1)节省内存和代码重用:当多个程序使用同一个函数库时,DLL可以减少在磁盘和物理内存中加载代码的重复量,且有助于代码的重用。

 

     (2)模块化:DLL有助于促进模块式程序开发。模块化允许仅仅更改几个应用程序共享使用的一个DLL中的代码和数据而不需要更改应用程序自身。适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试。这种模块化的基本形式允许如Microsoft Office、Microsoft Visual Studio、甚至windows自身这样大的应用程序使用较为紧凑的补丁和服务包。

       (3)扩展了应用程序的特性,使用dll文件可以使得应用程序能很方便的进行功能的扩展,很多程序的插件机制就是通过dll文件实现的。

     (4)可以用多种语言来编译和调用,由于各种语言都有自己独特的开发优势,在处理某类事务方面具有着独特的优势,所以在多种语言编程的过程中,可以利用dll文件作为桥梁,可以发挥多种语言的优点。

 

  缺点:DLL Hell:即DLL地狱,指几个应用程序在使用同一个共享的DLL库时发生版本冲突。

 

  究其原因,八个字:成也共用,败也共用。因为DLL Hell正是由于动态链接库可与其他程序共用函数、资源所导致。


 主要有两种情况

  设想这样一个场景:程序A会使用1.0版本的动态链接库X,则在程序A安装到系统时,会同时安装该1.0版本的动态链接库X。假设另一个程序B也会使用到动态链接库X,那么程序B直接复制到硬盘中即可正常运行,因为动态链接库已经存在于系统中。然而有一天,另一程序C也要使用动态链接库X,但是由于程序C开发的时间较晚,其需要较新版本---2.0版本的动态链接库X。则在程序C被安装到系统时,2.0版本的动态链接库X 也必须随之安装到系统中,此时系统中1.0版本的动态链接库将被2.0版本所取代(替换)。

 

  情况1:新版本的动态链接库不兼容旧版本。如,A何B需要X所提供的功能,在升级到2.0后,新版本的X竟然把此功能取消了(很难想象吧,呵呵但有时候就是如此....)。则此时虽然C能正常运行,但A和B均无法工作了。

  情况2:新版本的动态链接库兼容旧版本,但是存在一个bug。

如何生成C/C++的DLL文件

  1. 新建工程,选择“Visual C++”工程中的“Win32控制台程序”类型。工程设置中,将“应用程序类型”设置为“DLL”,“附加选项”设置为“空项目”。点击“完成”,则生产成了DLL工程文件。 

    这里写图片描述

  2. 这里以将C++类导出为DLL为例。类的头文件和源文件分别如下:

/**********************************************//*FileName:DllDemo.h *//**********************************************/#ifdef DllDemoAPI
#else#define DllDemoAPI _declspec(dllimport)
#endif

class Point
{public:    void Print(int *x, int *y, int xSize, int ySize);
};


extern "C"{
    DllDemoAPI Point *Point_Create();
    DllDemoAPI void Point_Print(Point *pt, int *x, int *y, int xSize, int ySize);
    DllDemoAPI void Point_Delete(Point *pt);
}12345678910111213141516171819202122
/**********************************************//*FileName:DllDemo.cpp                        *//**********************************************/#define DllDemoAPI _declspec(dllexport)
#include "DllTest.h"#include <stdio.h>
extern "C"{
    DllDemoAPI Point* Point_Create() 
    { 
        return new Point(); 
    }

    DllDemoAPI void Point_Print(Point* pPoint, int *x, int *y, int xSize, int ySize) 
    { 
        return pPoint->Print(x,y,xSize,ySize); 
    }

    DllDemoAPI void Point_Delete(Point* pPoint) 
    { 
        delete pPoint; 
    }
}void Point::Print(int *x,int *y, int xSize, int ySize)
{    for (int i = 0; i < xSize; i++)
    {
        printf("x[%d]=%d\n",i,x[i]);
    }    for (int i = 0; i < ySize; i++)
    {
        printf("y[%d]=%d\n",i,y[i]);
    }
}123456789101112131415161718192021222324252627282930313233343536

代码分析

  1. 代码中使用_declspec(dllimport)和_declspec(dllexport)进行DLL函数的导入和导出。

  2. extern “C”的作用是使用C语言的编译逻辑导出函数,即函数名称则为定义的名称。而不会向C++一样给函数名加上前缀和后缀。

  3. 正是因为使用C语言的编译逻辑导出函数,所以无法对类直接进行导出,因为C语言不支持类。当然如果生成的DLL是给C++使用的,则不需要extern “C”关键字,也可以对类等C++支持的结构进行导出。

  4. 这里默认的调用是cdecl,如果需要使用stdcall或者fastcall进行调用,则需要另外进行声明。

如何在C#中调用DLL文件

在C#中调用C++的DLL有多种方法,例如使用Marshal模块, 托管DLL, COM组件等。这里介绍的是使用最常见的P/Invoke方法。

在C#中调用上述工程的代码如下:

using System;using System.Collections.Generic;using System.Runtime.InteropServices;using System.Linq;using System.Text;

namespace ConsoleApplication
{
    class Program
    {
        [DllImport("DLLTest.dll", EntryPoint = "Point_Create", CallingConvention = CallingConvention.Cdecl)]        public static extern IntPtr Point_Create();
        [DllImport("DLLTest.dll", EntryPoint = "Point_Print", CallingConvention = CallingConvention.Cdecl)]        public static extern void Point_Print(IntPtr value, int[] x, int[] y, int xSize, int ySize);
        [DllImport("DLLTest.dll", EntryPoint = "Point_Delete", CallingConvention = CallingConvention.Cdecl)]        public static extern void Point_Delete(IntPtr value);        static void Main(string[] args)
        {
            IntPtr temp = Point_Create();            int[] x = new int[10];            int[] y = new int[10];            for (int i = 0; i < 10; i++)
            {
                x[i] = i;
                y[i] = i;
            }            int xSize = 10; 
            int ySize = 10;
            Point_Print(temp, x, y, xSize, ySize);
            Point_Delete(temp);
        }
    }
}123456789101112131415161718192021222324252627282930313233

代码分析

  1. 这里使用DllImport函数对DLL进行导入。

  2. 注意要将之前生成的DLL拷贝到exe文件所在的文件夹下。

如何调试DLL文件

生成DLL自然是为了调用,调用DLL有两种方式。

  静态调用:使用.h+.lib+.dll

  

复制代码
 1 #include <windows.h> 2 #include <iostream> 3 #include "DLL_DEMO.h" 4 using namespace std; 5 #pragma comment(lib, "DLL_DEMO.lib") 6  7 extern "C" _declspec(dllimport) int Add(int a, int b); 8 int main(int argc, char *argv[]) 9 {10     cout << Add(2, 3) << endl;11     system("pause");12     return 0;13 }
复制代码

  把头文件和lib文件、dll文件都放到跟源文件同一目录下即可使用。当然,路径可以重新设置。

  动态调用:仅使用dll文件

复制代码
 1 #include <windows.h> 2 #include <iostream> 3 using namespace std; 4 typedef int (*AddFunc)(int a, int b); 5 int main(int argc, char *argv[]) 6 { 7       HMODULE hDll = LoadLibrary(L"DLL_DEMO.dll"); 8       if (hDll != NULL) 9       {10             AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");11             if (add != NULL)12             {13                   cout<<add(2, 3)<<endl;14             }15             FreeLibrary(hDll);16       }17 }
复制代码

  在字符串前加一个L作用: unicode字符集是两个字节组成的。L告示编译器使用两个字节的 unicode 字符集。

  也可以使用dll来实现类和变量的共享,还可以实现内存共享。

本文地址:http://10tf.com/post/29.html
版权声明:本文为原创文章,版权归 andlly 所有,欢迎分享本文,转载请保留出处!

 发表评论


表情

还没有留言,还不快点抢沙发?