动态链接库的编写及使用

以前安装程序的时候,在安装目录下总会发现 好多的以.DLL结尾的文件,这些是什么玩意儿?有什么用?而且有时候运行程序的时候还会出现“无法定位程序输入点...与动态链接库....上”这种错误,现在想起来真是可笑。下面就来介绍一下动态链接库以及怎么使用动态链接库(以下简称DLL)。

当你编写一个程序的时候,为了使用以前写好的库中的函数,很方便的一个解决方法就是使用动态链接库(DLL),下面我们先来编写一个DLL文件,了解一下DLL文件的结构。

我使用的是RadASM 汇编集成工具,以前编写的都是WIN32汇编程序,现在编写DLL文件需要说明一下具体操作,如下图所示:首先在RadASM中点击新建工程出现以下界面,选中DLL Project一项,在下面书入工程名称,以及工程说明(根据自己的理解写。。)

下一步:

下一步:

该工具已经根据刚才的设定自己默认选择了一些必须要选中的选项如上图所示,如果我们需要在DLL文件中插入其它的一些东西的话,可以自己选择,比如资源(Rc),文本(Txt),注册表(Rgs)等等如上所示,创建目录下面那几个选项也是根据个人情况自己选择,下一步

在这里注意看一下下面编译资源脚本,编译,连接等后面的文本编辑框中的参数及其格式,编写WIN32APP与编写动态链接库的一些选项是不同的,该工具已经默认定义了必要的选项,如果个人需要修改可以自定义,点击完成

然后在28.ASM中输入DLL功能代码,与以前编写的WIN32APP不同的是还需要编辑一个28.Def文件,其实这个文件就是DLL的导出表信息,里面需要标明DLL中的每一个函数名称,为了使以后被应用程序使用的时候可以找到某个功能函数。

因为动态链接库的代码非常简单清晰,直接来看一下源代码

                .386

.model flat, stdcall

option casemap :none

include         windows.inc

.data?                                                                           ;动态链接库和WIN32APP的编写很像,这里基本                                                                                                            都是一样的

dwCounter       dd      ?

.code

DllEntry        proc    _hInstance,_dwReason,_dwReserved                    ;这里需要注意,这里是动态链接库                                                                                                                               的入口函数,就像是一个开始标志,                      
                                                                                                           只有这样才能被系统识别,虽然这                                                                                                                           个函数并没有什么功能代码,但是它
                                                                                                                              决定了DLL是否可以正常装入

mov     eax,TRUE

ret

DllEntry        Endp

_CheckCounter   proc                                                                   ;功能函数1,确保数字的大小在0--10之间

其实这个函数是个内部函数,因为在导出表                                                                                                    
              28.Def文件中并没有将它写出来,因此它只                                                                                                                    能用在DLL文件内部使用

mov     eax,dwCounter

cmp     eax,0

jge     @F                                                                                ;大于等于则跳转

xor     eax,eax

@@:

cmp     eax,10

jle     @F

mov     eax,10

@@:

mov     dwCounter,eax

ret

_CheckCounter endp

_IncCounter     proc                                                                         ;功能函数2,实现将当前的数字加1,该函数                                                                                                              在28.Def中已写出,后来会在应用程序中调用

inc     dwCounter

call    _CheckCounter

ret

_IncCounter endp

_DecCounter     proc                                                                              ;功能函数3,将当前的数字减1,该函数                                                                                                                  在28.Def中已写出,也会在应用程序中调用

dec     dwCounter

call    _CheckCounter

ret

_DecCounter endp

_Mod            proc    uses ecx edx _dwNumber1,_dwNumber2                ;功能函数4,求输入的两个数的模,

该函数也会在应用程序中调用

xor     edx,edx

mov     eax,_dwNumber1

mov     ecx,_dwNumber2

.if     ecx

div    ecx

mov    eax,edx

.endif

ret

_Mod endp

End     DllEntry                                                                               ;最后DLL的结尾

除此之外,还需要编写另一个文件那就是上面提到的28.Def文件,如下图所示,只需写上EXPORTS  后面加上在以后的程序中需要调用的函数的名称即可,看似很简单,但是好多错误都是在这里出现的。

然后编译,连接,构建,然后就可以在文件夹RadASM\Masm\Projects\28中找到已经编译好的DLL文件和一堆附属文件如下图:

上面的文件中除了一些我们认识的28.obj,28.rap,28.inc,28.asm还有Bak文件夹剩下的就是以前没见过的了,首先是28.dll文件,这就是我们自己编写的DLL文件(现在系统已经认识它了,而且我们也可以在应用程序中调用它了)。28.Def文件就是上面所说的DLL文件的导出函数说明,这是用来告诉连接器这三个函数需要导出,将来会被其他的的应用程序调用。28.lib文件是导入库文件,这个是为了当程序使用28.dll文件的时候,在源文件的开头 声明includelib   28.lib   (看着应该很熟悉这个格式,对,其实就是这样,为了让连接器在连接的时候知道到哪个库中寻找相应的函数,我们平时写的程序中的这样的头文件也是为了使系统的DLL文件只能够的函数可以被应用程序找到)
。 28.exp文件是一个附带的文件,对我们现在还没有用处。28.inc文件中书写的是导出函数的功能,以及其它信息(这些都是自己写的,为了让其他程序员使用的时候方便)。到此DLL文件的编写就结束了。

下面就来看一下,编写好的DLL文件怎样在应用程序中使用,首先编写资源文件,依旧使用ResEdit工具,

资源文件不在多说很熟悉了,直接看一下资源代码:

// Generated by ResEdit 1.6.6

// Copyright (C) 2006-2015

// http://www.resedit.net

#include <windows.h>

#include <commctrl.h>

#include <richedit.h>

#include "resource.h"

//

// Dialog resources

//

LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL

IDD_DIALOG1 DIALOG 0, 0, 229, 121

STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU

CAPTION "DLL例子"

FONT 8, "Ms Shell Dlg"

{

LTEXT           "%", 0, 80, 91, 6, 9, SS_LEFT, WS_EX_LEFT

LTEXT           "0", IDC_MOD, 183, 90, 30, 13, SS_LEFT, WS_EX_STATICEDGE

LTEXT           "=", 0, 165, 92, 5, 9, SS_LEFT, WS_EX_LEFT

EDITTEXT        IDC_NUM2, 99, 90, 52, 12, ES_AUTOHSCROLL, WS_EX_LEFT

EDITTEXT        IDC_NUM1, 25, 90, 48, 12, ES_AUTOHSCROLL, WS_EX_LEFT

GROUPBOX        "取模函数测试", 0, 16, 73, 208, 32, 0, WS_EX_STATICEDGE

LTEXT           "", IDC_COUNT, 31, 35, 100, 10, SS_LEFT, WS_EX_STATICEDGE

GROUPBOX        "DLL 内部计数器", 0, 16, 19, 206, 34, 0, WS_EX_STATICEDGE

PUSHBUTTON      "减少(&D)", IDC_DEC, 181, 32, 34, 14, 0, WS_EX_LEFT

DEFPUSHBUTTON   "增加(&A)", IDC_INC, 141, 32, 34, 14, 0, WS_EX_LEFT

}

//

// Icon resources

//

LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL

IDI_ICON1          ICON           "icon1.ico"

资源文件中除了徐亚输入的两个数字的位编辑文本框,其他的都是静态文本框,一些风格可以自己添加。

下面来看一下怎么来编写实现代码调用28.dll文件实现相关的函数功能。DLL文件的装入方法有两种,一种是直接使用导入库文件(28.lib)和inc文件(28.inc),但是要注意28.inc文件需要自己写入DLL导出库中的函数的声明

_IncCounterproto

_DecCounter proto

_Mod proto
dwNumber1:dword,dwNumber2:dword(就是这三个)就是这个格式。

28.lib文件是编写DLL文件时生成的直接复制到使用DLL文件的文件夹下就可以了。

这样其实也就相当于使用的是系统的DLL文件库一样,在头文件中加上包含的信息,在下面的代码中直接使用就可以了,这个程序很简单只是为了实现调用DLL文件,不再多说代码如下:

       .386

.model flat, stdcall

option casemap :none

include windows.inc

include user32.inc

includelib user32.lib

include kernel32.inc

includelib kernel32.lib

include         28.inc

includelib 28.lib

IDI_ICON1       equ         100

IDD_DIALOG1     equ         101

IDC_DEC         equ         40000

IDC_COUNT       equ         40001

IDC_INC         equ         40002

IDC_NUM1        equ         40003

IDC_NUM2        equ         40004

IDC_MOD         equ         40005

.code

_ProcDlgMain procuses ebx edi esi hWnd,wMsg,wParam,lParam

mov eax,wMsg

.if eax ==WM_CLOSE

invoke
EndDialog,hWnd,NULL

.elseif
eax == WM_COMMAND

mov eax,wParam

.if ax ==IDC_INC

invoke
_IncCounter                    ;在处理控件的消息的时候,直接调用DLL库中的函数,                                                                                              同下面两个函数的调用一样。

invoke
SetDlgItemInt,hWnd,IDC_COUNT,eax,FALSE

.elseif
ax == IDC_DEC

invoke
_DecCounter

invoke
SetDlgItemInt,hWnd,IDC_COUNT,eax,FALSE

.elseif ax ==
IDC_NUM1 || ax == IDC_NUM2

invoke
GetDlgItemInt,hWnd,IDC_NUM1,NULL,FALSE

push eax

invoke
GetDlgItemInt,hWnd,IDC_NUM2,NULL,FALSE

pop ecx

invoke
_Mod,ecx,eax

invoke
SetDlgItemInt,hWnd,IDC_MOD,eax,FALSE

.endif

.else

mov eax,FALSE

ret

.endif

mov eax,TRUE

ret

_ProcDlgMain endp

start:

invoke
GetModuleHandle,NULL

invoke
DialogBoxParam,eax,IDD_DIALOG1,NULL,offset _ProcDlgMain,NULL

invoke
ExitProcess,NULL

end start

DLL文件的另一种装入方式是动态装入,也就是不像上面那样,在调用程序前系统已经将DLL文件装入,而是自己在程序代码中使用相关的函数装入LoadLibrary(装入DLL文件)   GetProcAddress(获取导出函数地址)

在这个程序中遇见如下问题:

首先是自定义类型(typedef)

程序中的定义如下:

PROCVAR2       typedef     proto :dword,:dword                     ;首先定义一个使用两个参数的函数类型记为1

_PROCVAR0       typedef     proto                                              ;定义一个没有参数的函数类型记为2

PROCVAR2        typedef     ptr   _PROCVAR2                           ;定义一个1类型的指针作为一个新的类型

PROCVAR0        typedef     ptr   _PROCVAR0                           ;定义一个2类型的指针作为一个新的类型

这个新的类型的定义以前很少用到,这里需要注意学习使用。

还有一个问题就是在使用GetProcAddress函数获取了函数地址的时候,调用函数的方式是结合上面新的自定义类型来调用的,这种方式需要学习,如下代码:

.if      ax  ==  IDC_INC

.if      lpIncCounter                            ;首先判断lpincCounter是否为真,同下

invoke  lpIncCounter

invoke  SetDlgItemInt,hWnd,IDC_COUNT,eax,FALSE

.endif

.elseif  ax  ==  IDC_DEC

.if     lpDecCounter

invoke  lpDecCounter

invoke  SetDlgItemInt,hWnd,IDC_COUNT,eax,FALSE

.endif

.elseif  ax  ==  IDC_NUM1 || ax == IDC_NUM2

.if     lpMod

invoke  GetDlgItemInt,hWnd,IDC_NUM1,NULL,FALSE

push    eax

invoke  GetDlgItemInt,hWnd,IDC_NUM2,NULL,FALSE

pop     ecx

invoke  lpMod,ecx,eax

invoke  SetDlgItemInt,hWnd,IDC_MOD,eax,FALSE

下面来看一下程序代码:

                .386

.model flat,stdcall

option casemap :none

include         windows.inc

include         user32.inc

includelib      user32.lib

include         kernel32.inc

includelib      kernel32.lib

IDI_ICON1       equ         100

IDD_DIALOG1     equ         101

IDC_DEC         equ         40000

IDC_COUNT       equ         40001

IDC_INC         equ         40002

IDC_NUM1        equ         40003

IDC_NUM2        equ         40004

IDC_MOD         equ         40005

_PROCVAR2       typedef     proto :dword,:dword

_PROCVAR0       typedef     proto

PROCVAR2        typedef     ptr   _PROCVAR2

PROCVAR0        typedef     ptr   _PROCVAR0

.data?

hDllInstance    dd          ?

lpIncCounter    PROCVAR0    ?

lpDecCounter    PROCVAR0    ?

lpMod           PROCVAR2    ?

.const

szError         db          ‘28.dll 文件丢失或装载失败,程序功能无法实现‘,0

szDll           db          ‘28.dll‘,0

szIncCounter    db          ‘_IncCounter‘,0

szDecCounter    db          ‘_DecCounter‘,0

szMod           db          ‘_Mod‘,0

.code

_ProcDlgMain    proc        uses ebx edi esi hWnd,wMsg,wParam,lParam              ;对话框的消息处理过程

mov         eax,wMsg

.if         eax  ==  WM_CLOSE

.if      hDllInstance

xor       eax,eax

mov       lpIncCounter,eax

mov       lpDecCounter,eax

mov       lpMod,eax

invoke    FreeLibrary,hDllInstance

.endif

invoke   EndDialog,hWnd,NULL

.elseif     eax  ==  WM_INITDIALOG                                  ;在初始化消息中装入DLL文件

invoke   LoadLibrary,addr szDll

.if      eax

mov       hDllInstance,eax                                            ;装入后记得保存装入的DLL文件句柄

invoke    GetProcAddress,hDllInstance,addr szIncCounter    ;获得指定函数名称的的入口地址

mov       lpIncCounter,eax

invoke    GetProcAddress,hDllInstance,addr szDecCounter

mov       lpDecCounter,eax

invoke    GetProcAddress,hDllInstance,addr szMod

mov       lpMod,eax

.else

invoke    MessageBox,hWnd,addr szError,NULL,MB_OK or MB_ICONWARNING

invoke    GetDlgItem,hWnd,IDC_INC

invoke    EnableWindow,eax,FALSE                        ;装入失败则灰化所有按钮和两个编辑文本框

invoke    GetDlgItem,hWnd,IDC_DEC                            也就是两个需要输入数字的编辑控件

invoke    EnableWindow,eax,FALSE

invoke    GetDlgItem,hWnd,IDC_NUM1

invoke    EnableWindow,eax,FALSE

invoke    GetDlgItem,hWnd,IDC_NUM2

invoke    EnableWindow,eax,FALSE

.endif

.elseif     eax  ==  WM_COMMAND                            ;在处理控件的消息中,调用装入的DLL文件的

mov      eax,wParam                                          功能函数,注意调用方式,为了使用invoke

.if      ax  ==  IDC_INC                                           伪指令调用函数(可以检查函数的参数),自

.if      lpIncCounter                                            定义新的类型,以函数的入口地址调用函数

invoke  lpIncCounter

invoke  SetDlgItemInt,hWnd,IDC_COUNT,eax,FALSE

.endif

.elseif  ax  ==  IDC_DEC

.if     lpDecCounter

invoke  lpDecCounter

invoke  SetDlgItemInt,hWnd,IDC_COUNT,eax,FALSE

.endif

.elseif  ax  ==  IDC_NUM1 || ax == IDC_NUM2

.if     lpMod

invoke  GetDlgItemInt,hWnd,IDC_NUM1,NULL,FALSE

push    eax

invoke  GetDlgItemInt,hWnd,IDC_NUM2,NULL,FALSE

pop     ecx

invoke  lpMod,ecx,eax

invoke  SetDlgItemInt,hWnd,IDC_MOD,eax,FALSE

.endif

.endif

.else

mov      eax,FALSE

ret

.endif

mov         eax,TRUE

ret

_ProcDlgMain endp

start:

invoke      GetModuleHandle,NULL

invoke      DialogBoxParam,eax,IDD_DIALOG1,NULL,offset _ProcDlgMain,NULL

invoke      ExitProcess,NULL

end         start

最后来介绍一下新的API函数:

GetProcAddress()

功能:

检索指定的动态链接库(DLL)中的输出库函数地址

原型:

FARPROC GetProcAddress(

HMODULE hModule, // DLL模块句柄

LPCSTR lpProcName // 函数名

);

参数:

hModule

[in] 包含此函数的DLL模块的句柄。LoadLibrary、AfxLoadLibrary 或者GetModuleHandle函数可以返回此句柄。

lpProcName

[in] 包含函数名的以NULL结尾的字符串,或者指定函数的序数值。如果此参数是一个序数值,它必须在一个字的底字节,高字节必须为0。

返回值:

如果函数调用成功,返回值是DLL中的输出函数地址。

如果函数调用失败,返回值是NULL。得到进一步的错误信息,调用函数GetLastError。

LoadLibrary()

功能:

载入指定的动态链接库,并将它映射到当前进程使用的地址空间。一旦载入,即可访问库内保存的资源

原型:

HMODULE WINAPI LoadLibrary( _In_ LPCTSTR lpFileName);

参数:

lpFileName:  需要载入的动态链接库的名称

返回值:

Long,成功则返回库模块的句柄,零表示失败。会设置GetLastError

一旦不需要,用FreeLibrary函数释放DLL

FreeLibrary()

功能:

释放指定的动态链接库。

原型:

BOOL
WINAPI FreeLibrary( _In_ HMODULE hModule);

参数:

hLibModule
Long,要释放的一个库句柄

返回值:

long,非零表示成功,零表示失败。会设置GetLastError

转载请注明出处:http://blog.csdn.net/qq_22642239/article/details/51540403

时间: 2024-12-14 02:05:52

动态链接库的编写及使用的相关文章

安卓动态链接库系列-编写so

闲话少说,so的修改,重要性大家都知道,俗话说,磨刀不误砍柴功,不写写怎么逆的回来,这里从头编写so文件,分析so文件,修改so文件 今天先讲讲编写so 技术准备: jdk环境 jre环境 安卓开发环境 ndk开发环境 1.新建个工程,随便建 主函数界面入口 先这样放着 public class MainActivity extends ActionBarActivity { @Overrideprotected void onCreate(Bundle savedInstanceState)

动态链接库(Dynamic Link Library)学习笔记(附PE文件分析)

转载:http://www.cnblogs.com/yxin1322/archive/2008/03/08/donamiclinklibrary.html 作者:EricYou 转载请注明出处 注:本文所写的动态链接库指传统的DLL,并非是.NET中的Assembly. 我对动态链接和动态链接库的概念并不陌,但一直以来就停留在概念的层面上,没有更深入的了解.今天抽空看了一下有关动态链接和动态链接库的文章,有了一些新的认识,当然不能忘了写在这里.那么现在就开始... 什么是动态链接和动态链接库  

关于linux下GCC生成共享库(动态链接库)的探究

下面列出了我在对共享库(动态链接库)编写以及使用时遇到的几个简单问题进行探究和解答: 参考文档:http://www.cnblogs.com/likwo/archive/2012/05/09/2492225.html 1.静态库.动态链接库.共享库有什么区别? 静态库(windows下为.lib,linux下为.a)是在程序编写前就编译到目标程序中了,而动态链接库(windows下为.dll)可以在程序执行的任何时候被动态加载.共享库(linux下为.so)是在程序启动的时候加载到程序中. 1)

安卓第三方动态链接库so调用,解决未对java开放的函数调用,解决类名对齐问题

SO库为Android版本连接库(*.so文件)作用:以后只要开发提供给我们接口说明,我们就可以直接做接口测试了,不再需要一个个的抓包去分析,降低做接口测试的技术难度 一般项目里调用自己的so的方法是:将SO文件直接放到libs/armeabi下,然后代码中System.loadLibrary("xxx"):再public native static int xxx_xxx_xxx():接下来就可以直接调用xxx_xxx_xxx()方法: 但是有个问题就是,必须类名对齐,而且不能调未对

LoadRunner 调用dll方法

本文主要介绍简单DLL的编写方法及在LoadRunner中局部调用与全局调用DLL方法. 1.动态链接库的编写 在Visual C++6.0开发环境下,打开FileNewProject选项,可以选择Win32 Dynamic-Link Library建立一个空的DLL工程. DLL必须有一个入口点,这就象C语言MAIN函数一样.在Non-MFC DLL中DllMain是一个缺省的入口函数,你不需要编写自己的DLL入口函数,用这个缺省的入口函数就能使动态链接库被调用时得到正确的初始化.如果应用程序

C# 10分钟完成百度人脸识别——入门篇

嗨咯,小编在此祝大家新年快乐财多多! 今天我们来盘一盘人脸注册.人脸识别等相关操作,这是一个简单入门教程. 话不多说,我们进入主题: 完成人脸识别所需的步骤: 注册百度账号api,创建自己的应用: 创建vs控制台应用程序,引入动态链接库: 编写代码调试,效果图查看: 总结. 1.注册百度账号api,创建自己的应用 注册地址: https://login.bce.baidu.com/ 注册登录之后,在"产品服务" 菜单下找到人脸识别 ,如下图: 点击去创建自己的应用名称,其实最主要的就是

C# 10分钟完成百度图片提取文字(文字识别)——入门篇

现在图片文字识别已经很成熟了,比如qq长按图片,点击图片识别就可以识别图片的文字,将不认识的.文字数量大的.或者不能赋值的值进行二次可复制功能. 我们现在就基于百度Ai开放平台进行个人文字识别,demo使用的是C#控制台应用程序,后续有需要的可以嫁接到指定项目中使用,比如提供选择图片,点击识别, 获取返回的值.废话不多说,上干货: 总体为: 注册百度账号api,创建自己的应用: 创建vs控制台应用程序,引入动态链接库: 编写代码调试,效果图查看: 总结. 1.创建百度AI文字识别应用   在百度

利用curl进行逆地理编码_c语言编写动态链接库对PostgreSQL进行扩展

流程: [1]c语言编写逆地理编码的函数,利用curl库和高德服务器进行地理坐标解析 [2]gcc生成动态链接库 [3]postgreSQL中加载动态链接库中的函数 [4]postgreSQL中将逆地理编码函数的返回类型进行转化 =========================================== [1]c语言编写逆地理编码的函数,利用curl库和高德服务器进行地理坐标解析 #include <stdio.h> #include <stdlib.h> #inclu

dll(动态链接库)的编写

很可能有人会困惑dll究竟是什么,又改如何编写dll呢?今天,我就记录下编写以及调用动态链接库的步骤吧. 1.启动visuanl 6.0,新建工程 dll,如上图 2.在Source File中新建一个cpp文件 3.将如下代码复制到刚才新建的cpp中去 #include<stdio.h> extern "C" __declspec(dllexport) int Max(int i,int j) { return i>j?i:j; } extern "C&q