DLL动态链接库初学手迹(垃圾文,高手勿入)

原创 2004年07月23日 20:47:00

还在大三的时候写的一篇垃圾文章,磁盘里的删了,最后就放个尸体在这里吧,也算是活了一趟的见证……

总论

伴随着软件规模的扩大,一个系统不再能由一个或几个人从头到尾全部维护,模块化设计制作成了产业的共识。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

也许说到模块,您就会想起class,的确,类是一种模块,但它却仅仅是一个创建时的模块,每次对某一个模块的小小修改,就会导致对所有其他模块的重新编译。也许对于现在的您,这重新编译仅仅不过是代码之余的一次简短的休憩,然而,当您写的不再是一个个小小的习作,而是和别人一起开发一个大的系统,特别是对这个系统进行后期测试的时侯,您会发现,那每次半个小时以上的编译可以给您足够的理由发疯。

DLL是另一种模块,它是运行时的模块。你可以把类封装到一个个小小的DLL里面,一块块单独测试,最后再把它们拼接到一起。这不仅仅是省时间的选择,而且,很多具有强扩展性的模块还可以发布给别人,当然同样您也可以从别人那里获得具有某个功能的DLL模块,以节约开发时间和成本。您可以在很多大型软件里面看到成堆的DLL,这无疑是它强盛的生命力的证据。当然,一个应用程序附带的文件应当越少越好,否则调入一次应用程序就要打开数百个磁盘文件,估计谁都受不了。

正如开始接触WinMain时的那种敬畏心理,很多人在面对DLL时有些不知所措。其实现在写DLL已经不是什么困难的事情了,而MFCDLL也有很好的支持。嗯,好的,费话不多说,让我们开始旅途。

Dll技术基础

在介绍如何去编写DLL之前,您应该明白DLL是如何工作的。

DLL表现为磁盘上一个个文件,它无法自行启动,只能等待别人来调用它——或者更确切的说,载入它以调用它里面储存的数据和函数。如果要用DLL,必须要把它们映射到应用程序进程的地址空间去,这是显而易见的事情,DLL就像一个雇佣兵,你如果要让它为你卖命,起码应该去载入它到你的军队(进程地址空间)中来。请注意这里,DLL一旦调入,只会在内存中保留一份页面,无论多少对它们的调用仅仅不过是把它们映射,而不像EXE那样,一次运行,就是一个全新的空间。DLL的真实所占空间是一定的,这对内存的节约也有好处。如果您想对DLL更全面的认识,请参阅Jeffrey Richter的《Advanced Windows》(中文版译名:《Windows核心编程》机械工业出版社)

DLL的连接

DllMainDLL的入口,您可以将之类比于WinMainDllMain在连接到进程和断开与进程的连接和其他响应时被调用。如果没有它,我们导入的函数什么都不会做。具体细节,请参照《Windows核心编程》。

DLL可以隐式或者显式连接到进程中。在DLL中包含有一个导出函数表,客户程序装入DLL时,可以通过函数的符号化名字来得到这些函数,然后通过函数表得到这些函数在DLL模块内的地址,继而通过这些地址调用函数。

DLL中,我们通过以下的方式声明函数Fun是要被导出的:

extern “C” __declspec(dllexport) int Fun();

由于有些DLL需要调用别的DLL的函数,因此,某些DLL也会设置导入:

extern “C” __declspec(dllimport) int Fun2();

如果我们要使用刚刚的DLL导出的函数,需要这样:

extern “C” __declspec(dllimport) int Fun();

当然,同时需要把与Dll一起生成的Lib文件加入到工程中,而且,“客户程序必须至少调用了DLL导出函数中的一个函数”。LIB文件中记载的是DLL的导出符号,只有通过它我们才能够得知要调用哪些函数。在编译完进行链接的时候,LIB中的这些符号被匹配并绑定到EXE文件中,EXE同时保存下来LIB中的DLL文件名。当程序开始运行的时候,EXE去到下面几个地方找到DLL并装载,然后在运行时动态链接DLL中的功能:

1、当前运行进程的EXE的所在目录

2、进程当前目录

3、Windows系统目录(SystemSystem32

4、Windows目录

5、Path环境变量里列出的目录

显式连接不需要LIB文件,直接调用LoadLibrary(“DLL路径名”)就可以完成。如下:

HINSTANCE hInst;

hInstance = LoadLibary(“DLL路径名”);

如果要使用刚刚DLL导出的函数,现在需要这么做:

SORTPROC* pFun;

pFun = (SORTPROC*) GetProcAddress(hInstance , “Fun”);

int ret = (*pFun)();

显式连接的好处是可以根据需要在任何时候装载DLL,而隐式连接则在一开始就装载了所有的DLL

MFC来做DLL

正规的DLL和扩展的DLL

MFCAppWizard提供了两种DLL的支持:扩展的DLL和正规的DLL。正规的DLL可以被任何一个Win32开发环境装载,不过它只能导出C风格的函数,而不能导出C++类、成员函数或者重载函数,但我们可以在正规DLL中使用这些东西。

而扩展的DLL可以导出整个C++类,但是它要求比较高:首先客户程序必须动态链接到MFC库,而且和要用的扩展DLL连接到同一个版本的MFC DLL

一个正规DLL的例子

创建正规DLL时可以选择静态或者动态(使用MFC共享DLL)链接到MFC库。如果选择了静态链接,则DLL将包括所有它需要的MFC库代码的拷贝,这样一个DLL会比较大,但是可以独立运用,不再需要去考虑运行环境是否会有MFC支持。使用共享的MFC DLL则会小一些,但是必须保证客户机器上有相应的MFC DLL

下面就是一个生成使用MFC共享DLLDLL例子。

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

点击完成,主要生成了一个stddll.h和一个stddll.cpp

现在,在stddll.cpp中加入我们要导出的函数,假设是一个平方函数:

extern “C” __declspec(dllexport) int DllFunction(int val)

{

    AFX_MANAGE_STATE(AfxGetStaticModuleState());             //注意这一句一定要有

    return val*val;

}

下面我们需要测试这个DLL,新建一个MFC的对话框项目:

注意这里一定要选择当共享DLL

现在我们为这个对话框添加三个控件:一个按扭,两个编辑框。然后按照如下方式设置控件:

控件ID

数据

函数

IDC_CLICK

 

OnClick

IDC_IN

m_iInput

 

IDC_OUT

m_iOutput

 

首先我们要导入DllFunction,在TESTDLLDLG.H文件中添加:

extern “C” __declspec(dllimport) int DllFunction(int val);

OnClick函数如下:

void CTESTDLLDlg::OnClick()

{

       // TODO: Add your control notification handler code here

       UpdateData(TRUE);

       m_iOutput = DllFunction(m_iInput);

       UpdateData(FALSE);

}

 

好了,编译下,不成功对吧,呵呵,我们还没有告诉MFC去哪里找DLL呢,那该怎么办呢?先把刚刚DLL工程的Debug文件夹下面的STDDLL.dllSTDDLL.lib文件拷贝出来,STDDLL.dll放到系统文件夹(98Me的放到WindowsSystem文件夹下,NT2000XP的放到WinNTSystem32文件夹下),STDDLL.lib放到当前TESTDLL工程的Debug文件夹下,然后作如下设置,在“工程”菜单下面找到“设置”:

在“对象/库模块”里面添加那样一句就可以了,这里用的是相对工程文件夹的路径,如果您用了别的工程设置方式,只需要这里添上相对于工程相应的.dsp文件的相对路径就可以。

下面再运行就应该成功了,结果如下:

                

总结一下,DLL方要完成的任务:

1、生成正规DLL项目。

2、cpp文件中按格式添加所需要的函数。

Exe方要完成的任务:

1、cpp文件中完成对dll中函数的调用。

2、cpp相应的h文件中用__declspec(import)声明DLL要导出的函数。

3、完成对LIB路径的设置,并且最重要的,把DLL拷贝到EXE能找到它的地方。

其实在上面的例子中,你完全可以不把dll文件拷到系统目录下,而是拷贝到TESTDLL工程的Debug文件夹下,因为TESTDLL工程生成的Exe文件肯定在那里,这样,DLL就一定会在EXE可以找到的地方了。如果您有兴趣,还可以将DLL拷贝到别的地方试试看。

一个扩展DLL的例子

运行AppWizard,产生Projects中的MFCAppWizardDll),然后在紧接着的对话框中选择MFC Extension DLLMFC扩展DLL)。如图:

然后,会主要产生下面的代码和一个Def文件:

// EXTDLL.cpp : Defines the initialization routines for the DLL.

//

 

#include "stdafx.h"

#include <afxdllx.h>

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

 

 

static AFX_EXTENSION_MODULE EXTDLLDLL = { NULL, NULL };

 

extern "C" int APIENTRY

DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)

{

       // Remove this if you use lpReserved

       UNREFERENCED_PARAMETER(lpReserved);

 

       if (dwReason == DLL_PROCESS_ATTACH)

       {

              TRACE0("EXTDLL.DLL Initializing!/n");

             

              // Extension DLL one-time initialization

              if (!AfxInitExtensionModule(EXTDLLDLL, hInstance))

                     return 0;

//这里删掉了注释

 

              new CDynLinkLibrary(EXTDLLDLL);

       }

       else if (dwReason == DLL_PROCESS_DETACH)

       {

              TRACE0("EXTDLL.DLL Terminating!/n");

              // Terminate the library before destructors are called

              AfxTermExtensionModule(EXTDLLDLL);

       }

       return 1;   // ok

}

加入新的类:ExtClass.hExtClass.cpp,在ExtClass.h中添下如下代码:

#pragma once

 

 

#ifdef _WINDLL

#define DLL __declspec(dllexport)

#else

#define DLL

#endif

 

 

class DLL CExtDll

{

BOOL m_iNum;

public :

       CExtDll();

       ~CExtDll();

void DllMessageBox(LPSTR pszString);

void Func(int i);

int    ReturnVal();

 

} ;

.cpp中添加:

#include "stdafx.h"

#include "ExtClass.h"

 

 

CExtDll:: CExtDll ( )

{

       m_iNum = 255 ;

}

 

 

CExtDll:: ~CExtDll()

{ }

 

 

void CExtDll::DllMessageBox(LPSTR pszString)

{

       AfxMessageBox ( pszString ) ;

}

 

 

void CExtDll::Func(int i)

{

       m_iNum = i;

}

 

 

int    CExtDll::ReturnVal()

{

 return m_iNum;

}

然后编译,得到.lib.dll文件。

然后建立一个基于对话框的工程TestExtDll,设置基本与TestDll相同。然后做如下设置:

1、添加一个编辑框控件,利用Class Wizzard添加成员变量int类型的m_iNum

2、为确定按钮添加事件OnOK:在TestExtDllDlg.cpp中写下如下代码:

先是在最开头写一句:

#include “ExtClass.h”

这个.h文件应该从那个ExtDll工程中原封不动的拷贝过来。

然后为OnOK添加代码:

 

void CTestExtDllDlg::OnOK()

{

// TODO: Add extra validation here

UpdateData(TRUE);

//注意下面这几句,完成对Dll中类的调用

CExtDll  aDll;

aDll.DllMessageBox("Hello : )!!");

aDll.Func(88);

m_iNum = aDll.ReturnVal();

 

UpdateData(FALSE);

}

3、然后把ExtDll工程生成的.lib.dll文件如下处理:

3a、拷贝到当前TestExtDll工程的Debug文件夹下

3b、在工程-设置-link选项卡中,对“对象/库模块”中加入debug/ExtDll.lib

最后编译,结果如下:

看起来也不是很困难吧。

到这里我们已经把MFC Dll制作的基本思路说完了,如果对Dll仍有疑问,请参照《Visual C++技术内幕》(清华大学出版社)等经典教材。

外挂编写原理

一、 前言   所谓游戏外挂,其实是一种游戏外辅程序,它可以协助玩家自动产生游戏动作、修改游戏网络数据包以及修改游戏内存数据等,以实现玩家用最少的时间和金钱去完成功力升级和过关斩将。虽然,现在对游戏外...
  • s_ongfei
  • s_ongfei
  • 2007-07-31 16:28:00
  • 1712

了解HHOOK之路( 三)

接下来以一个简单的实例介绍HHOOK         在这里不得不吐槽一下当今的教育,这是我看的万方数据库中的一篇论文,我个人觉得应该具有权威性的,但是,事实是我按照它写的调试了很久都没有结果。...
  • a345719863
  • a345719863
  • 2016-11-25 10:31:25
  • 142

一种快速判断是否为质数的方法

public static boolean isprime(int x) ...{    if (x  7) ...{      if (x == 2 || x == 3 || x==5 || x =...
  • junli0310
  • junli0310
  • 2008-02-26 09:40:00
  • 1344

看人家小学生的素数的快速判断

#include bool isPrime(int num) { int i; if (num == 2 || num == 3) return true; if(num % 6 != 1...
  • u011518888
  • u011518888
  • 2013-08-13 19:19:47
  • 512

Windows消息拦截技术的应用

Windows消息拦截技术的应用民航合肥空管中心 周毅 一、前 言众所周知,Windows程式的运行是依靠发生的事件来驱动。换句话说,程式不断等待一个消息的发生,然后对这个消息的类型进行判断,再做适当...
  • Janlex
  • Janlex
  • 2007-04-05 17:44:00
  • 3126

游戏外挂设计技术探讨

网络游戏盛行催生了各种外挂,让商家头痛不已,那么外挂是怎么设计的呢?看看本文就知道了   一、 前言   所谓游戏外挂,其实是一种游戏外辅程序,它可以协助玩家自动产生游戏动作、修改游...
  • xiapang009
  • xiapang009
  • 2017-06-14 14:35:51
  • 204

dll入门简单实例(动态链接库)

#ifndef __NC_DLL_SAMPLE_H__ #define __NC_DLL_SAMPLE_H__ #ifdef WIN32 #ifdef DLL_SAMPLE_EXPORT ...
  • w280683395
  • w280683395
  • 2015-04-08 17:21:34
  • 2334

matlab封装DLL动态链接库 再用VS2010调用(matlab VS混合编程)

matlab封装DLL动态链接库 再用VS2010调用(matlab VS混合编程)(转载)    转载自:http://blog.csdn.net/cjl19880906/article/detai...
  • Striker_V
  • Striker_V
  • 2016-03-15 15:25:42
  • 1398

各种DLL制作方法

一.创建MFC 的常规DLL(设工程名为MyDLL1)(详工程F:/VcSample/DLL示例/DLL动态联接库之构共享内存)1.     新建工程MFC AppWizard(dll),选第二项- ...
  • guanchanghui
  • guanchanghui
  • 2007-05-22 15:54:00
  • 6113

消息钩子入门篇(4)---示例__外壳钩子(WH_SHELL)

这个例子程序完成记录系统所有启动的程序的功能,并保存到文件中。依然建立的是MFC扩展动态链接库。步骤同前面的例子。ShellHook.h文件中声明导出类class AFX_EXT_CLASS CShe...
  • jh2005
  • jh2005
  • 2007-05-19 09:35:00
  • 3265
收藏助手
不良信息举报
您举报文章:DLL动态链接库初学手迹(垃圾文,高手勿入)
举报原因:
原因补充:

(最多只允许输入30个字)