noslopforever [天堂里的死神]

我浴血奋战,只为了神圣永久不变的传言

用户操作
[即时聊天] [发私信] [加为好友]
noslopforever(天堂里的死神)ID:noslopforever
76238次访问,排名1397,好友5人,关注者13人。
3D、图形学、游戏、哲学、历史、音乐,一个都不能少。
noslopforever的文章
原创 85 篇
翻译 2 篇
转载 4 篇
评论 215 篇
noslopforever(天堂里的死神)的公告

-欢迎大家来到我的空间。这里关注游戏相关学科的问题。
-自我介绍:男,25岁,程序员,喜欢战争、历史和哲学题材游戏。作为一位普通的初学者,希望众位前辈们能多多包涵和帮助。
-欢迎大家拍砖。本Blog原创的文章,如要转载,请注明出处和姓名。本Blog放置的代码,大部分是伪码,不保证能够运行。
*留言本1:没有CSDN帐号的网友留言请点击此链接
*留言本2:CSDN网友请在个人空间留言 ^_^




烽火过千年,往事如烟。争斗一生归何处?黄土青山。 繁华总易逝,回首不堪。敢叫天地换新颜,铁马连天。 ——《无题》 李巍于2008年6月9日

-最近在做:做好自己的项目,安排自己的时间。

-有些栏目的文章是不放在主页显示的,如果有感兴趣的可以到相应栏目查询。杂项和Just As Gamer栏目的,仅作为个人喜好,恕不回复。

最近评论
RAINini:比我小一岁,比我厉害这么多,心里不平衡,请我吃饭。
RAINini:createDirectory好像不支持递归创建目录,../temp/temp 就创建失败了。
noslopforever:嘿嘿……奔三啦……
版本结束,再去酸菜鱼吧~~嘴馋了~~^o^
noslopforever:好的,多谢 ^_^,我马上去弄个1.36来
Nhsoft:你装啥老啊。藕都28了,faint.....竟然比我小三岁,快请客。
文章分类
收藏
相册
misc杂项
朝圣者的路途
文档所需图片册
我的书单
我的照片
!飞龙在天!
cproom前辈的Blog
eXtreme 3D —— Dreams的Blog(RSS)
flymemory的Blog
johnson的Blog——我的老师和第一个上司 ^_^
nhsoft——野猪大大的Blog
Nightmare of Design/Dev(RSS)
游戏编程实践——我的老师的Blog
马肝前辈的Blog
!虎狼成群!
亮——同学、引擎程序员
江自流——另一位同学兼才思敏捷的策划
游戏王——同学,一位才思敏捷的策划
推荐网页
Boost——C++准标准库
Boost中文站
GameDev.net
OGRE3D中文站
OGRE3D——开源的3D图形引擎
Sourceforge
有关WOW格式的Wiki
涂鸦软件——一个很牛的国产游戏引擎
喜欢的站点
《闪电战》杂志讨论区
帝国之鹰
德军总部
英雄世界
存档
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

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

新一篇: 从游戏规则的思考推开而去(牢骚文,做实事者请不要看) | 旧一篇: 图形是否是游戏的唯一??

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

总论

伴随着软件规模的扩大,一个系统不再能由一个或几个人从头到尾全部维护,模块化设计制作成了产业的共识。

也许说到模块,您就会想起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例子。

点击完成,主要生成了一个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++技术内幕》(清华大学出版社)等经典教材。

发表于 @ 2004年07月23日 20:47:00|评论(loading...)|编辑

新一篇: 从游戏规则的思考推开而去(牢骚文,做实事者请不要看) | 旧一篇: 图形是否是游戏的唯一??

评论

#blz_wowar 发表于2008-03-18 17:19:07  IP: 218.75.42.*
我最近也做过一些测试,
好像只要用到MFC的地方,而且是动态连接到MFC DLL的,就要 AFX_MANAGE_STATE 这个宏,静态的话就没什么问题了~
发表评论