动态链接库基础学习一

使用VC6.0创建一个空的动态链接库工程,建立一个空的文件,添加源文件

编写一个函数

如:int add (int a, int b)

{

     return a+b;

}

int subtract (int a, int b)

{

     return a-b;

}

这时,我们可以用VC6.0自带的工具dumpbin工具来查看此动态链接库导出的函数

当我们在cmd窗口下路径为C:/Program Files/Microsoft Visual Studio/MyProjects/Dll1/Debug>下执行dumpbin时出现以下错误:

C:/Program Files/Microsoft Visual Studio/MyProjects/KeyBoardDll/Debug>dumpbin

'dumpbin' 不是内部或外部命令,也不是可运行的程序

或批处理文件。

这种情况是由于系统无法找到dumbpin而产生的错误,解决方法:

C:/Program Files/Microsoft Visual Studio/VC98/Bin下找到VCVARS32.BAT,在cmd下执行此批处理,重新安装该工具执行环境即可,但仅在该cmd窗口下有小,关闭后必须重新执行该批处理

Dumpbin成功后如图:

 

 

C:/Program Files/Microsoft Visual Studio/MyProjects/Dll1/Debug>dumpbin  -exports dll1.dll

这时cmd窗口仅显示:

Summary

 

      7000 .data

      1000 .idata

      3000 .rdata

      2000 .reloc

     2A000 .text

没有导出的函数信息,这是为什么呢?

修改动态链接库函数:

_declspec(dllexport) int add (int a, int b)

{

     return a+b;

}

_declspec(dllexport) int subtract (int a, int b)

{

     return a-b;

}

 

重新执行dumpbin显示正确信息:

 

我们发现导出函数名很怪:

这里是不同的编译器会按照自己的规则把输出函数名字改变了,那么用其他编译器生成的程序在调用时会出现不能识别导出函数问题,该怎么办呢?

 

别慌,一会再讨论这个问题,下面我们先做一个简单的测试程序,

基于对话框的应用程序,放置2个按钮,一个为add,一个为subtract,添加命令消息响应函数,(仅拿add做实验)添加代码:

extern int add(int a,int b);

extern int subtract(int a,int b);//声明add方法是在外部的调用的函数

void CDllTestDlg::OnBtnAdd()

{

       // TODO: Add your control notification handler code here

       CString str;

       str.Format("5+3=%d,",add(5,3));

       MessageBox(str);

 }

当我们在连接时出现3个错误:

DllTest1Dlg.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z)

DllTest1Dlg.obj : error LNK2001: unresolved external symbol "int __cdecl subtract(int,int)" (?subtract@@YAHHH@Z)

原因:连接器不知道add函数在什么地方实现,所以出错。

解决方法:

我们可以复制输入库文件Dll1.lib文件到测试程序目录,并在工程---设置---LINK,在Object/library modules下写上:Dll1.lib,再次编译,没有出现问题。

Dll1.lib输入库文件作用:并没有包含实代码,只是用来为我们的连接程序提供信息,以便在我们的可执行文件(.Exe文件)中建立动态连接时的要用到的重定位表,我们可以查看可执行文件的输入信息,仍然使用dumpbin

C:/Program Files/Microsoft Visual Studio/MyProjects/Dll1/Debug>dumpbin  -imports dlltest.exe

可以看到有Dll1.dll的输出函数。

接下来我们总可以测试了吧,点击执行,出错!!!

 

说是找不到动态链接库,简单,我们把Dll1.dll复制到测试程序的目录下(工程目录和debug目录都可以),重新运行OK

这里,我们要提一下Dependency Walker了,它是VC6.0带的工具,我们可以在Microsoft

Visual C++ Tools中找到。它是图形化界面大大方便我们观察程序所依赖的DLL文件以及该Dll所依赖的其他DLL

我们把Dll1.dll放到DllTest程序的debug目录下,用dependency Walker打开DllTest;

 

                

我们可以改变extern int add(int a,int b);

extern int subtract(int a,int b);

为:

_declspec(dllimport) int add(int a,int b);

_declspec(dllimport) int subtract(int a,int b)执行,没有错误。

这里我们为什么要这样呢,原因如下:

添加_declspec(dllimport)告诉编译器我们所引用的符号是从动态链接库的.lib文件中输入的,编译器可以生成运行效率更高的代码,所以我们都应该用这个来声明从动态链接库调用的函数。

这里我们可以思考一个问题,如果我们把自己编写好的动态链接库提供给他人使用,他们如何得知,动态链接库中导出了哪些函数,那么他们不得不用一些工具来猜测了。

所以,为了解决这种情况,我们一般增加DLL文件的头文件,其中包含了注释和导出函数,方便他人使用。

增加DLL头文件,在头文件中写入

_declspec(dllimport) int add(int a,int b);

_declspec(dllimport) int subtract(int a,int b)

DllTest中注释掉_declspec(dllimport) int add(int a,int b);

_declspec(dllimport) int subtract(int a,int b)

Dll1.h复制到DllTest工程,并包含头文件include “Dll1.h”

当然,我们也可以改造Dll1.h让我们的Dll1.cpp也使用头文件

Dll1.h

#ifdef  DLL1_API

#else

#define DLL1_API _declspec(dllimport)

#endif

DLL1_API int add(int a,int b);

DLL1_API int subtract(int a,int b);

 

Dll1.cpp

#define DLL1_API _declspec(dllexport)

#include "Dll1.h"

 

DLL1_API int add(int a,int b)

{

       return a+b;

}

DLL1_API int subtract(int a,int b)

{

       return a-b;

}

还记得我们用dumpbin察看Dll1.dll时其中的导出函数名吗?它的名字非常的奇怪,如果我们使用其他的编译器来使用Dll1.dll文件时会出错,因为每种编译器按照不同的格式来规定其导出函数名字。如果我们不让函数名改变是不是就可以使我们的Dll1.dll变得通用了。

其实是可以的。在导出函数前加上cetern “C”extern “C” _declspec(dllimport) int add()

Dll1.h

#ifdef  DLL1_API

#else

#define DLL1_API extern “C” _declspec(dllimport)

#endif

DLL1_API int add(int a,int b);

DLL1_API int subtract(int a,int b);

 

Dll1.cpp

#define DLL1_API extern “C”_declspec(dllexport)

#include "Dll1.h"

 

DLL1_API int add(int a,int b)

{

       return a+b;

}

DLL1_API int subtract(int a,int b)

{

       return a-b;

}

 

 

但是,在我们导出函数的调用约定改变时,即使加上extern “C”,导出函数名也会发生改变。

我们在导出函数加上 _stdcall pascal调用

Dll1.h

#ifdef  DLL1_API

#else

#define DLL1_API extern “C” _declspec(dllimport)

#endif

DLL1_API int _stdcall add(int a,int b);

DLL1_API int _stdcall subtract(int a,int b);

 

Dll1.cpp

#define DLL1_API extern “C”_declspec(dllexport)

#include "Dll1.h"

 

DLL1_API int _stdcall add(int a,int b)

{

       return a+b;

}

DLL1_API int _stdcall subtract(int a,int b)

{

       return a-b;

}

这时,我们用dumpbin察看,函数名仍然发生改变:

 

还有其他的方法能够使Dll导出的函数名不发生改变吗?答案是有的

重新创建一个Dll2工程

Dll2.cpp

int add(int a,int b)

{

       return a+b;

}

int subtract(int a,int b)

{

       return a-b;

}

添加Dll2.def文件

LIBRARY Dll2

 

EXPORTS

add

subtract

这时用dumpbin察看,没有发生改变

 

这里,我们使用def文件即模块定义文件,并把它增加到Dll2工程中,

LIBRARY Dll2     指定我们动态链接库的内部名称,必须和Dll2.dll名相同。这句定义并不是必须的。

 

EXPORTS

add

subtract       指定要到处那些函数,并指定到处符号名,使我们导出的函数名不变。

更多EXPORTS用法查阅MSDN

 

以上均为隐式调用动态链接库,那么怎么动态的加载一个动态链接库呢?我们将继续探讨。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
elf(Executable and Linkable Format)是一种用于存储和加载可执行文件、共享库和核心转储的标准文件格式。elf文件格式是Linux系统中广泛使用的一种可执行文件格式,它包含程序的可执行代码、数据、符号表和重定位信息等。连接(Linking)是将程序的各个模块(源文件、库文件等)合并成一个可执行文件的过程。连接的主要任务是解析符号引用,确定符号的地址,并将各个模块的代码和数据组合成一个可执行文件。 在计算机系统基础实验中,学生通常会学习elf文件格式的结构和内容,以及连接的基本原理和过程。他们会了解elf文件的头部、程序头表和节区表等部分的结构,以及这些部分的作用和意义。他们还会学习连接的各个阶段,包括静态连接和动态连接的原理、过程和区别。通过实验,他们会掌握使用链接器(比如GNU ld)进行静态和动态连接的方法和技巧,并理解动态链接库的概念和使用。 通过elf与连接的实验,学生能够更深入地了解可执行文件的格式和结构,以及链接的过程和原理。他们可以通过实际操作和实验来加深对这些概念和原理的理解,为日后深入学习计算机系统和软件开发打下坚实的基础。同时,他们还可以通过实验掌握一些实用的技能和工具,比如使用符号表和重定位信息进行调试和优化,以及使用动态链接库来提高程序的模块化和可维护性。因此,elf与连接的实验在计算机系统基础教学中具有重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值