您的位置:首页 > 编程语言 > C语言/C++

DLL静态和动态调用

2017-01-02 19:15 267 查看

采用lib文件调用DLL(采用Lib文件的调用方式又被称为静态调用)

静态调用定义:

静态调用,也称为隐式调用,由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(Windows系统负责对DLL调用次数的计数),调用方式简单,能够满足通常的要求。通常采用的调用方式是把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,想使用DLL中的函数时,只须在源文件中声明一下。 LIB文件包含了每一个DLL导出函数的符号名和可选择的标识号以及DLL文件名,不含有实际的代码(这种lib文件被称为到入库,如果包含实际的可执行代码,则被称为静态库)。Lib文件包含的信息进入到生成的应用程序中,被调用的DLL文件会在应用程序加载时同时加载在到内存中。 

Lib文件的分类以及本质(重要)

程序编译的时候,链接器需要使用此文件。里面封装了具体的函数实现,变量定义等。而函数接口,变量申明还是通过.h文件给出来。目前以lib后缀的库有两种:一种为静态链接库(StaticLibary,以下简称“静态库”);另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import
Libary,以下简称“导入库”):注意另一种lib为DLL到入库。静态库和导入库(注意千万不要把lib文件的导入库,静态库的概念和DLL的动态调用,静态调用搞混淆了)的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表,各个函数地址等等,确保程序找到对应函数的一些基本地址信息。

静态Lib库(可执行文件中间文件):

静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。

导入lib库(符号集合)

有两个文件,而一个是导入库(.LIB)文件,一个是DLL文件,导入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。

如果既不想用静态Lib库,又不想用导入Lib库,就需要动态调用DLL了:需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了(动态调用DLL),前提是需要对DLL内部函数申明熟悉。

静态调用其步骤如下:

1.把你的youApp.DLL拷到你目标工程(需调用youApp.DLL的工程)的Debug目录下;

2.把你的youApp.lib拷到你目标工程(需调用youApp.DLL的工程)目录下;

3.把你的youApp.h(包含输出函数的定义)拷到你目标工程(需调用youApp.DLL的工程)目录下;

4.打开你的目标工程选中工程,选择Visual C++的Project主菜单的Settings菜单;

5.执行第4步后,VC将会弹出一个对话框,在对话框的多页显示控件中选择Link页。然后在Object/library
modules输入框中输入:youApp.lib

6.选择你的目标工程Head Files加入:youApp.h文件;

7.最后在你目标工程(*.cpp,需要调用DLL中的函数)中包含你的:#include
"youApp.h"

在Delphi中,无法使用VCC的DLL对应的lib文件,所以,需要自己在pas文件中采用pascal语法申明该API函数,如下所示:

FunctionAddNum(Num1,Num2:integer):integer;stdcall;external 'AddNum.dll'

name 'AddNumber';

在.Net中,静态使用DLL中,跟Delphi比较类似,如下图代码所示:

[DllImport(@".\ihrAPI\EmulinAPI.dll", CharSet =
CharSet.Ansi, CallingConvention =
CallingConvention.Cdecl)]
public static
extern int emulin_config();
 

使用LoadLibrary()使用DLL(采用LoadLibrary又被称为动态调用)

动态调用定义:

即显式调用方式,是由编程者用API函数加载和卸载DLL来达到调用DLL的目的,比较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式,实际需要的时候才会载入DLL,用完过后,释放此DLL。在Windows系统中,与动态库调用有关的函数包括: 

①LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。 

②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。 

③FreeLibrary(或MFC的AfxFreeLibrary),释放动态链接库

 

方法如下:

#include
"stdafx.h"
#include
"afx.h "
#include
<iostream>   //注意,不能加.h
#include <stdio.h>

 

typedef
int (*SQLITE_OPENTYPE)(
    const
char *filename,   /* Database filename(UTF-8) */
    sqlite3 **ppDb         
/* OUT: SQLite db handle */
);   //可知,必须知道DLL中内部函数的类型,因此也就不需要额外的lib文件了

 

SQLITE_OPENTYPE Func_Open;

/*Sample: use loadlibrary*/
    //HINSTANCE hInstLibrary =LoadLibrary("sqlite3.dll");
    HINSTANCE hInstLibrary =
LoadLibrary(TEXT("./sqlite3.dll"));
    if (hInstLibrary ==
NULL)
    {
        std::cout<<
"载入dll失败" << std::endl;
        FreeLibrary(hInstLibrary);
        system("pause");
        return 1;
    }
    Func_Open= (SQLITE_OPENTYPE)GetProcAddress(hInstLibrary,"sqlite3_open");
    if (Func_Open ==
NULL)//运行后程序进入这里,显示“转换失败”后结束
    {
        std::cout<<
"转换失败" << std::endl;
        FreeLibrary(hInstLibrary);
        system("pause");
        return 1;
    }
    //以下程序没有运行
    Func_Open("test1.db", &db);
    std::cout<< std::endl;
    FreeLibrary(hInstLibrary);

 

通过上述两个例程,已经完成理解了C++中DLL的编译和加载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息