无法定位kernel32.dll程序输入点ASave 于动态链接库 AutoDLL.DLL上。

无法定位程序输入点D3DXSaveTextureToFileInMemory于动态链接库D3DX9_27.DLL_百度知道
无法定位程序输入点D3DXSaveTextureToFileInMemory于动态链接库D3DX9_27.DLL
点三国无双会跳出来,哪位大哥可以帮忙解答
提问者采纳
开始-运行-regsvr32 %systemroot%\system32\WMADMOD.DLL
其他类似问题
20人觉得有用
为您推荐:
定位程序输入点的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁3Dmax无法定位程序输入点【急】_百度知道
提问者采纳
1. 请先运行“!设置.bat”,然后运行 3dsmax.exe,用附带的注册机算号进行注册。。。2. 要删除,请先运行“卸载.bat”,然后手动删除所有文件。
重要:若你没有安装其它 AutoDesk 的产品,可以删除 x:\Program Files\Common Files\Autodesk Shared 文件夹中的所有文件,某些文件可能需要重新启动 Windows 后才能删除。(x: 代表你的系统盘符。)测试平台: Windows XP SP2, 不保证其它平台或其它系统下运行正常!^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Service Pack 2 所解决的问题: 第三方• 在具有巴西材质的场景上执行“文件/重置”时发生故障 (SP 2)。批处理渲染• 文件的像素纵横比现在可被正确保存 (SP 1)。BIPED• 保留“体形模式”的问题已被解决 (SP 1)。• 在没有关键点的轨迹上粘贴动画姿势的功能已可正常使用 (SP 1)。• “足迹模式”的行走循环问题已被纠正 (SP 1)。• 已能为 biped 的颈部设置四元数关键点 (SP 1)。• 有关更改轨迹到 Euler 然后退出体形模式的故障问题 (SP 1)。• 蒙皮在保存及重新打开场景后损坏的 Physique 和“扭曲”链接相关问题。 这个问题已被解决 (SP 1)。• 骨盆在体形模式中的 Biped 问题 (SP 2)。• 固定关键帧的插值更改 (SP 2)。• 在旧版本 Max 的 biped 上使用“清除动画”时发生故障 (SP 2)。• 执行创建集合、复制姿态、撤销、全部重做时发生故障 (SP 2)。• 手部变换转移到体形模式 (SP 2)。CLOTH• 在 Cloth 修改器中调整密度时发生故障 (SP 2)。• Cloth 在添加实例几何体时发生故障 (SP 2)。控制器• 在 Max 8 中,有一项新功能(参阅轨迹视图控制器菜单 & 忽略/考虑动画范围)可让用户指定参数控制器的范围。 不过,噪波控制器不被支持。 这个问题已被纠正 (SP 1)。DIRECT X• DirectX 中的 CheckVertex 回归 (SP 2)。• 法线凹凸贴图在未使用任何位图时显示错误的法线 (SP 2)。• 编辑材质编辑器所使用的 .fx 文件时发生故障 (SP 2)。• 无法读取材质库因为当中包含 DirectX 材质 (SP 2)。• DirectX 中的 CheckVertex 故障 (SP 2)。• .fx 文件显示中不必要的更改 (SP 2)。显示• 过度放大时视口背景出现内存不足的故障问题
(SP 2)。• 使用栅格捕捉时出现的内存不足故障问题已被解决 (SP 2)。• 栅格捕捉功能现可用于细分的栅格间距 (SP 1)。编辑样条线• 使用栅格捕捉进行优化的精度问题已被解决;不过不建议在此工作流程中使用摄影机和透视视图 (SP 1)。文件 I/O• 插件 .ini 文件在启动时被重新排序 (SP 2)。• 保存 OpenEXR 使锁定文件被覆盖的问题已被解决 (SP 1)。头发• 使用 mental ray 渲染头发时发生故障 (SP 2)。• 使用 mental ray 时头发和 32-位虚拟帧缓冲区出现不兼容问题 (SP 2)。• 涉及样条线头发和子对象模式的故障已被修复 (SP 1)。加载/保存动画• 之前在 3ds Max 8 中,保存一个或多个控制器具有注释轨迹副控制器的动画会发生故障 – 这个问题已被解决 (SP 1)。• 有关在之前保存的 xmm 文件中更改当前/引入链时的已知贴图对话框重定位卷展栏问题已被解决 (SP 1)。• 下列 maxscript 命令之前无法运作:loadSaveAnimation.loadAnimation &c:\\ProgramFiles\\Autodesk\\3dsmax8\\animations\\test.xaf& $'Sphere01'现在已被纠正 (SP 1)。材质编辑器• 有关调整位图放置设置时发生故障的问题已被解决 (SP 1)。• 有关启用标准材质以 DX 显示,然后单击“在视口中显示贴图”时发生故障的问题已被解决 (SP 1)。MAX SCRIPT• MaxScript 中获取/设置 ActiveX 的方法差异 (SP 2)。• 有关鼠标轨迹功能发出未定义错误消息的问题已被解决 (SP 1)。• 有关使用 maxscript 添加位置脚本控制器到对象,然后造成系统异常的问题已被解决 (SP 1)。• 之前在 3dsmax 7 中,若将包含错误的脚本拖放到视口将会繁殖一个高亮显示错误线的 maxscript 编辑窗口。 这并未在 3dsmax 8 中发生,这个问题现在已被纠正 (SP 1)。MENTAL RAY• 使用 mental ray 在混合贴图内启用光线跟踪会使人工品被渲染 (SP 2)。• 当使用 mental ray 渲染时,面贴图对象上的人工品将缺少纹理坐标 (SP 2)。• 使用 mental ray 时,接缝出现具有人工品的光贴图 (SP 2)。• 用户从附属渲染所使用的帐户注销时发生故障 (SP 2)。• 双面、顶/底和混合材质的已知人工品问题已被解决 (SP 1)。网格平滑修改器• 涉及 reactor cloth 和网格平滑“渲染迭代”的故障问题已被解决 (SP 1)。运动混合器• 3ds Max 8 无法加载 3ds Max 7 .mix 文件的问题已被解决 (SP 1)。变形器修改器• 有关交换变形通道和撤销/重做操作的故障问题已被解决 (SP 1)。粒子流• 在移动按住控制关键点而创建的粒子流发射器时发生故障 (SP 2)。• 创建无因次粒子流源时发生故障 (SP 2)。渲染• 渲染之前所做出的 QuickTime 设置更改被忽略 (SP 2)。• 保存包含批处理渲染数据的场景时发生故障 (SP 2)。网络渲染• 在通过命令行对场景进行恢复场景状况的网络渲染时,会弹出重复的材质对话框 (SP 2)。• 无法在 UNC 位置存在渲染预设时提交批处理渲染工作 (SP 2)。• 有关保存使用网络渲染器的场景时文件损坏的问题已被解决 (SP 1)。渲染到纹理• 使用渲染到纹理条带渲染更改输出路径时失败 (SP 2)。 脚本控制器• 涉及脚本控制器变量在控制器目标被更改后失去连接的问题已被解决 (SP 1)。• 有关在保存和加载脚本后进行撤销的故障问题已被解决 (SP 1)。SDK • SetBipedRot 无法在姿势模式中用于脚、锁骨和手指 (SP 2)。• 涉及 SDK 编译采样文件 IGameExporter.vcproj 和 DX 材质的故障问题已被解决 (SP 1)。蒙皮• 还原到之前骨骼影响限制值的问题已被解决 (SP 2)。• 顶点绝对权重微调器的更新问题已被解决 (SP 2)。轨迹视图• 由于 GDI 对象泄漏所造成的轨迹视图故障 (SP 2)。UV 展开• 纹理查找表中的未初始化代码不再使软件发生故障 (SP 2)。• 执行子对象选择后打开编辑 UV 对话框时的故障问题已被修复 (SP 2)。• 所涵括的一项修复可让展开打开包含“无”材质的多个子对象材质 (SP 1)。• 做出选择更改后使面子对象层级的贴图更改丢失的缺陷已被解决 (SP 1)。顶点绘制修改器• 顶点绘制修改器中的笔刷预设图标如今可在法文和德文操作系统上正确显示 (SP 1)。^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
其他类似问题
为您推荐:
定位程序输入点的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁1888人阅读
  先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
&&& 对动态链接库,我们还需建立如下概念:
  (1)DLL 的编制与具体的编程语言及编译器无关
  只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。
  (2)动态链接库随处可见
  我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。
  一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。由此可见DLL对我们来说其实并不陌生。
  (3)VC动态链接库的分类
  Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。
  非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL
包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。
  由于本文篇幅较长,内容较多,势必需要先对阅读本文的有关事项进行说明,下面以问答形式给出。
  问:本文主要讲解什么内容?
  答:本文详细介绍了DLL编程的方方面面,努力学完本文应可以对DLL有较全面的掌握,并能编写大多数DLL程序。
  问:如何看本文?
  答:本文每一个主题的讲解都附带了源代码例程,可以随文下载(每个工程都经WINRAR压缩)。所有这些例程都由笔者编写并在VC++6.0中调试通过。
  当然看懂本文不是读者的最终目的,读者应亲自动手实践才能真正掌握DLL的奥妙。
  问:学习本文需要什么样的基础知识?
  答:如果你掌握了C,并大致掌握了C++,了解一点MFC的知识,就可以轻松地看懂本文。
  2.静态链接库
  对静态链接库的讲解不是本文的重点,但是在具体讲解DLL之前,通过一个静态链接库的例子可以快速地帮助我们建立“库”的概念。
图1 建立一个静态链接库
  如图1,在VC++6.0中new一个名称为libTest的static library工程(单击此处下载本工程),并新建lib.h和lib.cpp两个文件,lib.h和lib.cpp的源代码如下:
//文件:lib.h
#ifndef LIB_H
#define LIB_H
extern &C& int add(int x,int y);   //声明为C编译、连接方式的外部函数
//文件:lib.cpp
#include &lib.h&
int add(int x,int y)
 return x +
  编译这个工程就得到了一个.lib文件,这个文件就是一个函数库,它提供了add的功能。将头文件和.lib文件提交给用户后,用户就可以直接使用其中的add函数了。
  标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。
  下面来看看怎么使用这个库,在libTest工程所在的工作区内new一个libCall工程。libCall工程仅包含一个main.cpp文件,它演示了静态链接库的调用方法,其源代码如下:
#include &stdio.h&
#include &..\lib.h&
#pragma comment( lib, &..\\debug\\libTest.lib& )  //指定与静态库一起连接
int main(int argc, char* argv[])
 printf( &2 + 3 = %d&, add( 2, 3 ) );
  静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。代码中#pragma comment( lib , &..\\debug\\libTest.lib& )的意思是指本文件生成的.obj文件应与libTest.lib一起连接。如果不用#pragma comment指定,则可以直接在VC++中设置,如图2,依次选择tools、options、directories、library files菜单或选项,填入库文件路径。图2中加红圈的部分为我们添加的libTest.lib文件的路径。
图2 在VC中设置库文件路径
  这个静态链接库的例子至少让我们明白了库函数是怎么回事,它们是哪来的。我们现在有下列模糊认识了:
  (1)库不是个怪物,编写库的程序和编写一般的程序区别不大,只是库不能单独执行;
  (2)库提供一些可以给别的程序调用的东东,别的程序要调用它必须以某种方式指明它要调用之。
  以上从静态链接库分析而得到的对库的懵懂概念可以直接引申到动态链接库中,动态链接库与静态链接库在编写和调用上的不同体现在库的外部接口定义及调用方式略有差异。
&&& 3.库的调试与查看
  在具体进入各类DLL的详细阐述之前,有必要对库文件的调试与查看方法进行一下介绍,因为从下一节开始我们将面对大量的例子工程。
  由于库文件不能单独执行,因而在按下F5(开始debug模式执行)或CTRL+F5(运行)执行时,其弹出如图3所示的对话框,要求用户输入可执行文件的路径来启动库函数的执行。这个时候我们输入要调用该库的EXE文件的路径就可以对库进行调试了,其调试技巧与一般应用工程的调试一样。
图3 库的调试与“运行”
  通常有比上述做法更好的调试途径,那就是将库工程和应用工程(调用库的工程)放置在同一VC工作区,只对应用工程进行调试,在应用工程调用库中函数的语句处设置断点,执行后按下F11,这样就单步进入了库中的函数。第2节中的libTest和libCall工程就放在了同一工作区,其工程结构如图4所示。
图4 把库工程和调用库的工程放入同一工作区进行调试
  上述调试方法对静态链接库和动态链接库而言是一致的。所以本文提供下载的所有源代码中都包含了库工程和调用库的工程,这二者都被包含在一个工作区内,这是笔者提供这种打包下载的用意所在。
  动态链接库中的导出接口可以使用Visual C++的Depends工具进行查看,让我们用Depends打开系统目录中的user32.dll,看到了吧?红圈内的就是几个版本的MessageBox了!原来它真的在这里啊,原来它就在这里啊!
图5 用Depends查看DLL
  当然Depends工具也可以显示DLL的层次结构,若用它打开一个可执行文件则可以看出这个可执行文件调用了哪些DLL。
  好,让我们正式进入动态链接库的世界,先来看看最一般的DLL,即非MFC DLL。
4.1一个简单的DLL
  第2节给出了以静态链接库方式提供add函数接口的方法,接下来我们来看看怎样用动态链接库实现一个同样功能的add函数。
&&& 如图6,在VC++中new一个Win32 Dynamic-Link Library工程dllTest()。注意不要选择MFC AppWizard(dll),因为用MFC AppWizard(dll)建立的将是第5、6节要讲述的MFC 动态链接库。
图6 建立一个非MFC DLL
  在建立的工程中添加lib.h及lib.cpp文件,源代码如下:
/* 文件名:lib.h */
#ifndef LIB_H
#define LIB_H
extern &C& int __declspec(dllexport)add(int x, int y);
/* 文件名:lib.cpp */
#include &lib.h&
int add(int x, int y)
 return x +
  与第2节对静态链接库的调用相似,我们也建立一个与DLL工程处于同一工作区的应用工程dllCall,它调用DLL中的函数add,其源代码如下:
#include &stdio.h&
#include &windows.h&
typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
int main(int argc, char *argv[])
 HINSTANCE hD //DLL句柄
 lpAddFun addF //函数指针
 hDll = LoadLibrary(&..\\Debug\\dllTest.dll&);
 if (hDll != NULL)
  addFun = (lpAddFun)GetProcAddress(hDll, &add&);
  if (addFun != NULL)
   int result = addFun(2, 3);
   printf(&%d&, result);
  FreeLibrary(hDll);
 return 0;
  分析上述代码,dllTest工程中的lib.cpp文件与第2节静态链接库版本完全相同,不同在于lib.h对函数add的声明前面添加了__declspec(dllexport)语句。这个语句的含义是声明函数add为DLL的导出函数。DLL内的函数分为两种:
  (1)DLL导出函数,可供应用程序调用;
  (2) DLL内部函数,只能在DLL程序使用,应用程序无法调用它们。
  而应用程序对本DLL的调用和对第2节静态链接库的调用却有较大差异,下面我们来逐一分析。
  首先,语句typedef int ( * lpAddFun)(int,int)定义了一个与add函数接受参数类型和返回值均相同的函数指针类型。随后,在main函数中定义了lpAddFun的实例addFun;
  其次,在函数main中定义了一个DLL HINSTANCE句柄实例hDll,通过Win32 Api函数LoadLibrary动态加载了DLL模块并将DLL模块句柄赋给了hDll;
  再次,在函数main中通过Win32 Api函数GetProcAddress得到了所加载DLL模块中函数add的地址并赋给了addFun。经由函数指针addFun进行了对DLL中add函数的调用;
  最后,应用工程使用完DLL后,在函数main中通过Win32 Api函数FreeLibrary释放了已经加载的DLL模块。
  通过这个简单的例子,我们获知DLL定义和调用的一般概念:
  (1)DLL中需以某种特定的方式声明导出函数(或变量、类);
  (2)应用工程需以某种特定的方式调用DLL的导出函数(或变量、类)。
  下面我们来对“特定的方式进行”阐述。
  4.2 声明导出函数
  DLL中导出函数的声明有两种方式:一种为4.1节例子中给出的在函数声明中加上__declspec(dllexport),这里不再举例说明;另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。
  下面的代码演示了怎样同.def文件将函数add声明为DLL导出函数(需在dllTest工程中添加lib.def文件):
; lib.def : 导出DLL函数
LIBRARY dllTest
  .def文件的规则为:
  (1)LIBRARY语句说明.def文件相应的DLL;
  (2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
  (3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。
  由此可以看出,例子中lib.def文件的含义为生成名为“dllTest”的动态链接库,导出其中的add函数,并指定add函数的序号为1。
  4.3 DLL的调用方式
  在4.1节的例子中我们看到了由“LoadLibrary-GetProcAddress-FreeLibrary”系统Api提供的三位一体“DLL加载-DLL函数地址获取-DLL释放”方式,这种调用方式称为DLL的动态调用。
  动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式链接在运行时决定加载哪个 DLL 文件。
  与动态调用方式相对应的就是静态调用方式,“有动必有静”,这来源于物质世界的对立统一。“动与静”,其对立与统一竟无数次在技术领域里得到验证,譬如静态IP与DHCP、静态路由与动态路由等。从前文我们已经知道,库也分为静态库与动态库DLL,而想不到,深入到DLL内部,其调用方式也分为静态与动态。“动与静”,无处不在。《周易》已认识到有动必有静的动静平衡观,《易.系辞》曰:“动静有常,刚柔断矣”。哲学意味着一种普遍的真理,因此,我们经常可以在枯燥的技术领域看到哲学的影子。
  静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则Windows对DLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。静态调用方式简单实用,但不如动态调用方式灵活。
  下面我们来看看静态调用的例子(),将编译dllTest工程所生成的.lib和.dll文件拷入dllCall工程所在的路径,dllCall执行下列代码:
#pragma comment(lib,&dllTest.lib&)
//.lib文件中仅仅是关于其对应DLL文件中函数的重定位信息
extern &C& __declspec(dllimport) add(int x,int y);
int main(int argc, char* argv[])
 int result = add(2,3);
 printf(&%d&,result);
 return 0;
  由上述代码可以看出,静态调用方式的顺利进行需要完成两个动作:
  (1)告诉编译器与DLL相对应的.lib文件所在的路径及文件名,#pragma comment(lib,&dllTest.lib&)就是起这个作用。
  程序员在建立一个DLL文件时,连接器会自动为其生成一个对应的.lib文件,该文件包含了DLL 导出函数的符号名及序号(并不含有实际的代码)。在应用程序里,.lib文件将作为DLL的替代文件参与编译。
  (2)声明导入函数,extern &C& __declspec(dllimport) add(int x,int y)语句中的__declspec(dllimport)发挥这个作用。
  静态调用方式不再需要使用系统API来加载、卸载DLL以及获取DLL中导出函数的地址。这是因为,当程序员通过静态链接方式编译生成应用程序时,应用程序中调用的与.lib文件中导出符号相匹配的函数符号将进入到生成的EXE 文件中,.lib文件中所包含的与之对应的DLL文件的文件名也被编译器存储在
EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL 函数的动态链接。这样,EXE将能直接通过函数名调用DLL的输出函数,就象调用程序内部的其他函数一样。
4.4 DllMain函数
  Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数、WIN32程序需要WinMain函数一样。在前面的例子中,DLL并没有提供DllMain函数,应用工程也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。
  根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。
  我们来看一个DllMain函数的例子()。
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
 switch (ul_reason_for_call)
  case DLL_PROCESS_ATTACH:
   printf(&\nprocess attach of dll&);
  case DLL_THREAD_ATTACH:
   printf(&\nthread attach of dll&);
  case DLL_THREAD_DETACH:
   printf(&\nthread detach of dll&);
  case DLL_PROCESS_DETACH:
   printf(&\nprocess detach of dll&);
 return TRUE;
  DllMain函数在DLL被加载和卸载时被调用,在单个线程启动和终止时,DLLMain函数也被调用,ul_reason_for_call指明了被调用的原因。原因共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和THREAD_DETACH,以switch语句列出。
  来仔细解读一下DllMain的函数头BOOL APIENTRY DllMain( HANDLE hModule, WORD ul_reason_for_call, LPVOID lpReserved )。
  APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;
  进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了DLL模块在进程虚拟空间中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数hModule的来历。
  执行下列代码:
hDll = LoadLibrary(&..\\Debug\\dllTest.dll&);
if (hDll != NULL)
 addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));
 //MAKEINTRESOURCE直接使用导出文件中的序号
 if (addFun != NULL)
  int result = addFun(2, 3);
  printf(&\ncall add in dll:%d&, result);
 FreeLibrary(hDll);
  我们看到输出顺序为:
process attach of dll
call add in dll:5
process detach of dll
  这一输出顺序验证了DllMain被调用的时机。
  代码中的GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意,它直接通过.def文件中为add函数指定的顺序号访问add函数,具体体现在MAKEINTRESOURCE ( 1 ),MAKEINTRESOURCE是一个通过序号获取函数名的宏,定义为(节选自winuser.h):
#define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))
#define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i)))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#define MAKEINTRESOURCE MAKEINTRESOURCEA
  4.5 __stdcall约定
  如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern &C&),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而__cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
  Windows编程中常见的几种函数类型声明宏都是与__stdcall和__cdecl有关的(节选自windef.h):
#define CALLBACK __stdcall //这就是传说中的回调函数
#define WINAPI __stdcall //这就是传说中的WINAPI
#define WINAPIV __cdecl
#define APIENTRY WINAPI //DllMain的入口就在这里
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
  在lib.h中,应这样声明add函数:
int __stdcall add(int x, int y);
  在应用工程中函数指针类型应定义为:
typedef int(__stdcall *lpAddFun)(int, int);
  若在lib.h中将函数声明为__stdcall调用,而应用工程中仍使用typedef int (* lpAddFun)(int,int),运行时将发生错误(因为类型不匹配,在应用工程中仍然是缺省的__cdecl调用),弹出如图7所示的对话框。
图7 调用约定不匹配时的运行错误
  图8中的那段话实际上已经给出了错误的原因,即“This is usually a result of …”。
&&& 4.6 DLL导出变量
  DLL定义的全局变量可以被调用进程访问;DLL也可以访问调用进程的全局数据,我们来看看在应用工程中引用DLL中变量的例子()。
/* 文件名:lib.h */
#ifndef LIB_H
#define LIB_H
extern int dllGlobalV
/* 文件名:lib.cpp */
#include &lib.h&
#include &windows.h&
int dllGlobalV
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
 switch (ul_reason_for_call)
  case DLL_PROCESS_ATTACH:
   dllGlobalVar = 100; //在dll被加载时,赋全局变量为100
  case DLL_THREAD_ATTACH:
  case DLL_THREAD_DETACH:
  case DLL_PROCESS_DETACH:
 return TRUE;
  ;文件名:lib.def
  ;在DLL中导出变量
LIBRARY &dllTest&
dllGlobalVar CONSTANT
;或dllGlobalVar DATA
GetGlobalVar
  从lib.h和lib.cpp中可以看出,全局变量在DLL中的定义和使用方法与一般的程序设计是一样的。若要导出某全局变量,我们需要在.def文件的EXPORTS后添加:
  变量名 CONSTANT   //过时的方法
  变量名 DATA     //VC++提示的新方法
  在主函数中引用DLL中定义的全局变量:
#include &stdio.h&
#pragma comment(lib,&dllTest.lib&)
extern int dllGlobalV
int main(int argc, char *argv[])
 printf(&%d &, *(int*)dllGlobalVar);
 *(int*)dllGlobalVar = 1;
 printf(&%d &, *(int*)dllGlobalVar);
 return 0;
  特别要注意的是用extern int dllGlobalVar声明所导入的并不是DLL中全局变量本身,而是其地址,应用程序必须通过强制指针转换来使用DLL中的全局变量。这一点,从*(int*)dllGlobalVar可以看出。因此在采用这种方式引用DLL全局变量时,千万不要进行这样的赋值操作:
dllGlobalVar = 1;
  其结果是dllGlobalVar指针的内容发生变化,程序中以后再也引用不到DLL中的全局变量了。
  在应用工程中引用DLL中全局变量的一个更好方法是:
#include &stdio.h&
#pragma comment(lib,&dllTest.lib&)
extern int _declspec(dllimport) dllGlobalV //用_declspec(dllimport)导入
int main(int argc, char *argv[])
 printf(&%d &, dllGlobalVar);
 dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换
 printf(&%d &, dllGlobalVar);
 return 0;
  通过_declspec(dllimport)方式导入的就是DLL中全局变量本身而不再是其地址了,笔者建议在一切可能的情况下都使用这种方式。
  4.7 DLL导出类
  DLL中定义的类可以在应用工程中使用。
  下面的例子里,我们在DLL中定义了point和circle两个类,并在应用工程中引用了它们()。
//文件名:point.h,point类的声明
#ifndef POINT_H
#define POINT_H
#ifdef DLL_FILE
 class _declspec(dllexport) point //导出类point
 class _declspec(dllimport) point //导入类point
  point();
  point(float x_coordinate, float y_coordinate);
//文件名:point.cpp,point类的实现
#ifndef DLL_FILE
 #define DLL_FILE
#include &point.h&
//类point的缺省构造函数
point::point()
 x = 0.0;
 y = 0.0;
//类point的构造函数
point::point(float x_coordinate, float y_coordinate)
//文件名:circle.h,circle类的声明
#ifndef CIRCLE_H
#define CIRCLE_H
#include &point.h&
#ifdef DLL_FILE
class _declspec(dllexport)circle //导出类circle
class _declspec(dllimport)circle //导入类circle
  void SetCentre(const point &centrePoint);
  void SetRadius(float r);
  float GetGirth();
  float GetArea();
  circle();
 private:
//文件名:circle.cpp,circle类的实现
#ifndef DLL_FILE
#define DLL_FILE
#include &circle.h&
#define PI 3.1415926
//circle类的构造函数
circle::circle()
 centre = point(0, 0);
 radius = 0;
//得到圆的面积
float circle::GetArea()
 return PI *radius *
//得到圆的周长
float circle::GetGirth()
 return 2 *PI *
//设置圆心坐标
void circle::SetCentre(const point &centrePoint)
 centre = centreP
//设置圆的半径
void circle::SetRadius(float r)
 radius =
  类的引用:
#include &..\circle.h&  //包含类声明头文件
#pragma comment(lib,&dllTest.lib&);
int main(int argc, char *argv[])
 point p(2.0, 2.0);
 c.SetCentre(p);
 c.SetRadius(1.0);
 printf(&area:%f girth:%f&, c.GetArea(), c.GetGirth());
 return 0;
  从上述源代码可以看出,由于在DLL的类实现代码中定义了宏DLL_FILE,故在DLL的实现中所包含的类声明实际上为:
class _declspec(dllexport) point //导出类point
class _declspec(dllexport) circle //导出类circle
  而在应用工程中没有定义DLL_FILE,故其包含point.h和circle.h后引入的类声明为:
class _declspec(dllimport) point //导入类point
class _declspec(dllimport) circle //导入类circle
  不错,正是通过DLL中的
class _declspec(dllexport) class_name //导出类circle 
  与应用程序中的
class _declspec(dllimport) class_name //导入类
  匹对来完成类的导出和导入的!
  我们往往通过在类的声明头文件中用一个宏来决定使其编译为class _declspec(dllexport) class_name还是class _declspec(dllimport) class_name版本,这样就不再需要两个头文件。本程序中使用的是:
#ifdef DLL_FILE
 class _declspec(dllexport) class_name //导出类
 class _declspec(dllimport) class_name //导入类
  实际上,在MFC DLL的讲解中,您将看到比这更简便的方法,而此处仅仅是为了说明_declspec(dllexport)与_declspec(dllimport)匹对的问题。
  由此可见,应用工程中几乎可以看到DLL中的一切,包括函数、变量以及类,这就是DLL所要提供的强大能力。只要DLL释放这些接口,应用程序使用它就将如同使用本工程中的程序一样!
  本章虽以VC++为平台讲解非MFC DLL,但是这些普遍的概念在其它语言及开发环境中也是相同的,其思维方式可以直接过渡。接下来,我们将要研究MFC规则DLL。
&5. MFC规则DLL
  5.1 概述
  MFC规则DLL的概念体现在两方面:
  (1) 它是MFC的
  “是MFC的”意味着可以在这种DLL的内部使用MFC;
  (2) 它是规则的
  “是规则的”意味着它不同于MFC扩展DLL,在MFC规则DLL的内部虽然可以使用MFC,但是其与应用程序的接口不能是MFC。而MFC扩展DLL与应用程序的接口可以是MFC,可以从MFC扩展DLL中导出一个MFC类的派生类。
  Regular DLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。在这种动态连接库中,包含一个从CWinApp继承下来的类,DllMain函数则由MFC自动提供。
  Regular DLL分为两类:
  (1)静态链接到MFC 的规则DLL
  静态链接到MFC的规则DLL与MFC库(包括MFC扩展 DLL)静态链接,将MFC库的代码直接生成在.dll文件中。在调用这种DLL的接口时,MFC使用DLL的资源。因此,在静态链接到MFC 的规则DLL中不需要进行模块状态的切换。
  使用这种方法生成的规则DLL其程序较大,也可能包含重复的代码。
  (2)动态链接到MFC 的规则DLL
  动态链接到MFC 的规则DLL 可以和使用它的可执行文件同时动态链接到 MFC DLL 和任何MFC扩展 DLL。在使用了MFC共享库的时候,默认情况下,MFC使用主应用程序的资源句柄来加载资源模板。这样,当DLL和应用程序中存在相同ID的资源时(即所谓的资源重复问题),系统可能不能获得正确的资源。因此,对于共享MFC
DLL的规则DLL,我们必须进行模块切换以使得MFC能够找到正确的资源模板。
  我们可以在Visual C++中设置MFC规则DLL是静态链接到MFC DLL还是动态链接到MFC DLL。如图8,依次选择Visual C++的project -& Settings -& General菜单或选项,在Microsoft Foundation Classes中进行设置。
图8 设置动态/静态链接MFC DLL
  5.2 MFC规则DLL的创建
  我们来一步步讲述使用MFC向导创建MFC规则DLL的过程,首先新建一个project,如图9,选择project的类型为MFC AppWizard(dll)。点击OK进入如图10所示的对话框。
图9 MFC DLL工程的创建
图10所示对话框中的1区选择MFC DLL的类别。
  2区选择是否支持automation(自动化)技术, automation 允许用户在一个应用程序中操纵另外一个应用程序或组件。例如,我们可以在应用程序中利用 Microsoft Word 或Microsoft Excel的工具,而这种使用对用户而言是透明的。自动化技术可以大大简化和加快应用程序的开发。
  3区选择是否支持Windows Sockets,当选择此项目时,应用程序能在 TCP/IP 网络上进行通信。 CWinApp派生类的InitInstance成员函数会初始化通讯端的支持,同时工程中的StdAfx.h文件会自动include &AfxSock.h&头文件。
  添加socket通讯支持后的InitInstance成员函数如下:
BOOL CRegularDllSocketApp::InitInstance()
 if (!AfxSocketInit())
  AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
  return FALSE;
 return TRUE;
  4区选择是否由MFC向导自动在源代码中添加注释,一般我们选择“Yes,please”。
图10 MFC DLL的创建选项
5.3 一个简单的MFC规则DLL
  这个DLL的例子(属于静态链接到MFC 的规则DLL)中提供了一个如图11所示的对话框。
图11 MFC规则DLL例子
  在DLL中添加对话框的方式与在MFC应用程序中是一样的。在图11所示DLL中的对话框的Hello按钮上点击时将MessageBox一个“Hello,pconline的网友”对话框,下面是相关的文件及源代码,其中删除了MFC向导自动生成的绝大多数注释(下载本工程):
  第一组文件:CWinApp继承类的声明与实现
// RegularDll.h : main header file for the REGULARDLL DLL
#if !defined(AFX_REGULARDLL_H__3E9CB22B_588B__B3416ADB79B3__INCLUDED_)
#define AFX_REGULARDLL_H__3E9CB22B_588B__B3416ADB79B3__INCLUDED_
#if _MSC_VER & 1000
#pragma once
#endif // _MSC_VER & 1000
#ifndef __AFXWIN_H__
#error include ’stdafx.h’ before including this file for PCH
#include &resource.h& // main symbols
class CRegularDllApp : public CWinApp
  CRegularDllApp();
  DECLARE_MESSAGE_MAP()
// RegularDll.cpp : Defines the initialization routines for the DLL.
#include &stdafx.h&
#include &RegularDll.h&
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
BEGIN_MESSAGE_MAP(CRegularDllApp, CWinApp)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CRegularDllApp construction
CRegularDllApp::CRegularDllApp()
/////////////////////////////////////////////////////////////////////////////
// The one and only CRegularDllApp object
CRegularDllApp theA
  分析:
  在这一组文件中定义了一个继承自CWinApp的类CRegularDllApp,并同时定义了其的一个实例theApp。乍一看,您会以为它是一个MFC应用程序,因为MFC应用程序也包含这样的在工程名后添加“App”组成类名的类(并继承自CWinApp类),也定义了这个类的一个全局实例theApp。
  我们知道,在MFC应用程序中CWinApp取代了SDK程序中WinMain的地位,SDK程序WinMain所完成的工作由CWinApp的三个函数完成:
virtual BOOL InitApplication( );
virtual BOOL InitInstance( );
virtual BOOL Run( ); //传说中MFC程序的“活水源头”
  但是MFC规则DLL并不是MFC应用程序,它所继承自CWinApp的类不包含消息循环。这是因为,MFC规则DLL不包含CWinApp::Run机制,主消息泵仍然由应用程序拥有。如果DLL 生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从DLL 导出的函数来调用PreTranslateMessage成员函数。
  另外,MFC规则DLL与MFC 应用程序中一样,需要将所有 DLL中元素的初始化放到InitInstance 成员函数中。
  第二组文件 自定义对话框类声明及实现
#if !defined(AFX_DLLDIALOG_H__CEA4C6AF_245D_48A6_B11A_A5521EAD7C4E__INCLUDED_)
#define AFX_DLLDIALOG_H__CEA4C6AF_245D_48A6_B11A_A5521EAD7C4E__INCLUDED_
#if _MSC_VER & 1000
#pragma once
#endif // _MSC_VER & 1000
// DllDialog.h : header file
/////////////////////////////////////////////////////////////////////////////
// CDllDialog dialog
class CDllDialog : public CDialog
 // Construction
  CDllDialog(CWnd* pParent = NULL); // standard constructor
  enum { IDD = IDD_DLL_DIALOG };
 protected:
  virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  // Implementation
 protected:
  afx_msg void OnHelloButton();
  DECLARE_MESSAGE_MAP()
// DllDialog.cpp : implementation file
#include &stdafx.h&
#include &RegularDll.h&
#include &DllDialog.h&
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
/////////////////////////////////////////////////////////////////////////////
// CDllDialog dialog
CDllDialog::CDllDialog(CWnd* pParent /*=NULL*/)
: CDialog(CDllDialog::IDD, pParent)
void CDllDialog::DoDataExchange(CDataExchange* pDX)
 CDialog::DoDataExchange(pDX);
BEGIN_MESSAGE_MAP(CDllDialog, CDialog)
 ON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDllDialog message handlers
void CDllDialog::OnHelloButton()
 MessageBox(&Hello,pconline的网友&,&pconline&);
  分析:
  这一部分的编程与一般的应用程序根本没有什么不同,我们照样可以利用MFC类向导来自动为对话框上的控件添加事件。MFC类向导照样会生成类似ON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)的消息映射宏。
  第三组文件 DLL中的资源文件
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by RegularDll.rc
#define IDD_DLL_DIALOG 1000
#define IDC_HELLO_BUTTON 1000
  分析:
  在MFC规则DLL中使用资源也与在MFC应用程序中使用资源没有什么不同,我们照样可以用Visual C++的资源编辑工具进行资源的添加、删除和属性的更改。
  第四组文件 MFC规则DLL接口函数
#include &StdAfx.h&
#include &DllDialog.h&
extern &C& __declspec(dllexport) void ShowDlg(void)
 CDllDialog dllD
 dllDialog.DoModal();
  分析:
  这个接口并不使用MFC,但是在其中却可以调用MFC扩展类CdllDialog的函数,这体现了“规则”的概类。
  与非MFC DLL完全相同,我们可以使用__declspec(dllexport)声明或在.def中引出的方式导出MFC规则DLL中的接口。
5.4 MFC规则DLL的调用
  笔者编写了如图12的对话框MFC程序(下载本工程)来调用5.3节的MFC规则DLL,在这个程序的对话框上点击“调用DLL”按钮时弹出5.3节MFC规则DLL中的对话框。
图12 MFC规则DLL的调用例子
  下面是“调用DLL”按钮单击事件的消息处理函数:
void CRegularDllCallDlg::OnCalldllButton()
 typedef void (*lpFun)(void);
 HINSTANCE hD //DLL句柄
 hDll = LoadLibrary(&RegularDll.dll&);
 if (NULL==hDll)
  MessageBox(&DLL加载失败&);
 lpFun addF //函数指针
 lpFun pShowDlg = (lpFun)GetProcAddress(hDll,&ShowDlg&);
 if (NULL==pShowDlg)
  MessageBox(&DLL中函数寻找失败&);
 pShowDlg();
  上述例子中给出的是显示调用的方式,可以看出,其调用方式与第4节中非MFC DLL的调用方式没有什么不同。
  我们照样可以在EXE程序中隐式调用MFC规则DLL,只需要将DLL工程生成的.lib文件和.dll文件拷入当前工程所在的目录,并在RegularDllCallDlg.cpp文件(图12所示对话框类的实现文件)的顶部添加:
#pragma comment(lib,&RegularDll.lib&)
void ShowDlg(void);
  并将void CRegularDllCallDlg::OnCalldllButton() 改为:
void CRegularDllCallDlg::OnCalldllButton()
 ShowDlg();
  5.5 共享MFC DLL的规则DLL的模块切换
  应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的HINSTANCE。应用程序在加载DLL时对其进行了重定位。
  共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。
  这次我们创建一个动态链接到MFC DLL的规则DLL(下载本工程),在其中包含如图13的对话框。
图13 DLL中的对话框
  另外,在与这个DLL相同的工作区中生成一个基于对话框的MFC程序,其对话框与图12完全一样。但是在此工程中我们另外添加了一个如图14的对话框。
图14 EXE中的对话框
  图13和图14中的对话框除了caption不同(以示区别)以外,其它的都相同。
  尤其值得特别注意,在DLL和EXE中我们对图13和图14的对话框使用了相同的资源ID=2000,在DLL和EXE工程的resource.h中分别有如下的宏:
//DLL中对话框的ID
#define IDD_DLL_DIALOG 2000
//EXE中对话框的ID
#define IDD_EXE_DIALOG 2000
  与5.3节静态链接MFC DLL的规则DLL相同,我们还是在规则DLL中定义接口函数ShowDlg,原型如下:
#include &StdAfx.h&
#include &SharedDll.h&
void ShowDlg(void)
 CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
 dlg.DoModal();
  而为应用工程主对话框的“调用DLL”的单击事件添加如下消息处理函数:
void CSharedDllCallDlg::OnCalldllButton()
 ShowDlg();
  我们以为单击“调用DLL”会弹出如图13所示DLL中的对话框,可是可怕的事情发生了,我们看到是图14所示EXE中的对话框!
  产生这个问题的根源在于应用程序与MFC规则DLL共享MFC DLL(或MFC扩展DLL)的程序总是默认使用EXE的资源,我们必须进行资源模块句柄的切换,其实现方法有三:
  方法一 在DLL接口函数中使用:
AFX_MANAGE_STATE(AfxGetStaticModuleState());
  我们将DLL中的接口函数ShowDlg改为:
void ShowDlg(void)
 //方法1:在函数开始处变更,在函数结束时恢复
 //将AFX_MANAGE_STATE(AfxGetStaticModuleState());作为接口函数的第一//条语句进行模块状态切换
 AFX_MANAGE_STATE(AfxGetStaticModuleState());
 CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框
 dlg.DoModal();
  这次我们再点击EXE程序中的“调用DLL”按钮,弹出的是DLL中的如图13的对话框!嘿嘿,弹出了正确的对话框资源。
  AfxGetStaticModuleState是一个函数,其原型为:
AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );
  该函数的功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类(模块全局数据也就是模块状态)的实例,对其进行设置,并将其指针pModuleState返回。
  AFX_MODULE_STATE类的原型如下:
// AFX_MODULE_STATE (global data for a module)
class AFX_MODULE_STATE : public CNoTrackObject
  #ifdef _AFXDLL
   AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion);
   AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion,BOOL bSystem);
   AFX_MODULE_STATE(BOOL bDLL);
  #endif
  ~AFX_MODULE_STATE();
  CWinApp* m_pCurrentWinA
  HINSTANCE m_hCurrentInstanceH
  HINSTANCE m_hCurrentResourceH
  LPCTSTR m_lpszCurrentAppN
  … //省略后面的部分
  AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作,类似汇编中call指令对pc指针和sp寄存器的保存与恢复、中断服务程序的中断现场压栈与恢复以及操作系统线程调度的任务控制块保存与恢复。
  许多看似不着边际的知识点居然有惊人的相似!
  AFX_MANAGE_STATE是一个宏,其原型为:
AFX_MANAGE_STATE( AFX_MODULE_STATE* pModuleState )
  该宏用于将pModuleState设置为当前的有效模块状态。当离开该宏的作用域时(也就离开了pModuleState所指向栈上对象的作用域),先前的模块状态将由AFX_MODULE_STATE的析构函数恢复。
  方法二 在DLL接口函数中使用:
AfxGetResourceHandle();
AfxSetResourceHandle(HINSTANCE xxx);
  AfxGetResourceHandle用于获取当前资源模块句柄,而AfxSetResourceHandle则用于设置程序目前要使用的资源模块句柄。
  我们将DLL中的接口函数ShowDlg改为:
void ShowDlg(void)
 //方法2的状态变更
 HINSTANCE save_hInstance = AfxGetResourceHandle();
 AfxSetResourceHandle(theApp.m_hInstance);
 CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框
 dlg.DoModal();
 //方法2的状态还原
 AfxSetResourceHandle(save_hInstance);
  通过AfxGetResourceHandle和AfxSetResourceHandle的合理变更,我们能够灵活地设置程序的资源模块句柄,而方法一则只能在DLL接口函数退出的时候才会恢复模块句柄。方法二则不同,如果将ShowDlg改为:
extern CSharedDllApp theA //需要声明theApp外部全局变量
void ShowDlg(void)
 //方法2的状态变更
 HINSTANCE save_hInstance = AfxGetResourceHandle();
 AfxSetResourceHandle(theApp.m_hInstance);
 CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框
 dlg.DoModal();
 //方法2的状态还原
 AfxSetResourceHandle(save_hInstance);
 //使用方法2后在此处再进行操作针对的将是应用程序的资源
 CDialog dlg1(IDD_DLL_DIALOG); //打开ID为2000的对话框
 dlg1.DoModal();
  在应用程序主对话框的“调用DLL”按钮上点击,将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图14)。
  方法三 由应用程序自身切换
  资源模块的切换除了可以由DLL接口函数完成以外,由应用程序自身也能完成(下载本工程)。
  现在我们把DLL中的接口函数改为最简单的:
void ShowDlg(void)
 CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
 dlg.DoModal();
  而将应用程序的OnCalldllButton函数改为:
void CSharedDllCallDlg::OnCalldllButton()
 //方法3:由应用程序本身进行状态切换
 //获取EXE模块句柄
 HINSTANCE exe_hInstance = GetModuleHandle(NULL);
 //或者HINSTANCE exe_hInstance = AfxGetResourceHandle();
 //获取DLL模块句柄
 HINSTANCE dll_hInstance = GetModuleHandle(&SharedDll.dll&);
 AfxSetResourceHandle(dll_hInstance); //切换状态
 ShowDlg(); //此时显示的是DLL的对话框
 AfxSetResourceHandle(exe_hInstance); //恢复状态
 //资源模块恢复后再调用ShowDlg
 ShowDlg(); //此时显示的是EXE的对话框
  方法三中的Win32函数GetModuleHandle可以根据DLL的文件名获取DLL的模块句柄。如果需要得到EXE模块的句柄,则应调用带有Null参数的GetModuleHandle。
  方法三与方法二的不同在于方法三是在应用程序中利用AfxGetResourceHandle和AfxSetResourceHandle进行资源模块句柄切换的。同样地,在应用程序主对话框的“调用DLL”按钮上点击,也将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图14)。
  在下一节我们将对MFC扩展DLL进行详细分析和实例讲解,欢迎您继续关注本系列连载。
 前文我们对非MFC DLL和MFC规则DLL进行了介绍,现在开始详细分析DLL的最后一种类型――MFC扩展DLL。
  6.1概论
  MFC扩展DLL与MFC规则DLL的相同点在于在两种DLL的内部都可以使用MFC类库,其不同点在于MFC扩展DLL与应用程序的接口可以是MFC的。MFC扩展DLL的含义在于它是MFC的扩展,其主要功能是实现从现有MFC库类中派生出可重用的类。MFC扩展DLL使用MFC 动态链接库版本,因此只有用共享MFC 版本生成的MFC 可执行文件(应用程序或规则DLL)才能使用MFC扩展DLL。
&&& 从前文可知,MFC规则DLL被MFC向导自动添加了一个CWinApp的对象,而MFC扩展DLL则不包含该对象,它只是被自动添加了DllMain 函数。对于MFC扩展DLL,开发人员必须在DLL的DllMain函数中添加初始化和结束代码。
  从下表我们可以看出三种DLL对DllMain入口函数的不同处理方式:
非 MFC DLL
编程者提供DllMain函数
MFC规则 DLL
CWinApp对象的InitInstance 和 ExitInstance
MFC扩展 DLL
MFC DLL向导生成DllMain 函数
  对于MFC扩展DLL,系统会自动在工程中添加如下表所示的宏,这些宏为DLL和应用程序的编写提供了方便。像AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA这样的宏,在DLL和应用程序中将具有不同的定义,这取决于_AFXEXT宏是否被定义。这使得在DLL和应用程序中,使用统一的一个宏就可以表示出输出和输入的不同意思。在DLL中,表示输出(因为_AFXEXT被定义,通常是在编译器的标识参数中指定/D_AFXEXT);在应用程序中,则表示输入(_AFXEXT没有定义)。
AFX_CLASS_IMPORT
__declspec(dllexport)
AFX_API_IMPORT
__declspec(dllexport)
AFX_DATA_IMPORT
__declspec(dllexport)
AFX_CLASS_EXPORT
__declspec(dllexport)
AFX_API_EXPORT
__declspec(dllexport)
AFX_DATA_EXPORT
__declspec(dllexport)
AFX_EXT_CLASS
#ifdef _AFXEXT
 AFX_CLASS_EXPORT
 AFX_CLASS_IMPORT
AFX_EXT_API
#ifdef _AFXEXT
 AFX_API_EXPORT
 AFX_API_IMPORT
AFX_EXT_DATA
#ifdef _AFXEXT
 AFX_DATA_EXPORT
 AFX_DATA_IMPORT
  6.2 MFC扩展DLL导出MFC派生类
  在这个例子中,我们将产生一个名为“ExtDll”的MFC扩展DLL工程,在这个DLL中导出一个对话框类,这个对话框类派生自MFC类CDialog。
  使用MFC向导生成MFC扩展DLL时,系统会自动添加如下代码:
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 );
 //说明:lpReserved是一个被系统所保留的参数,对于隐式链接是一个非零值,对于显式链接值是零
 if (dwReason == DLL_PROCESS_ATTACH)
  TRACE0( &EXTDLL.DLL Initializing!\n& );
  // Extension DLL one-time initialization
  if ( !AfxInitExtensionModule( ExtDllDLL, hInstance ))
   return 0;
   // Insert this DLL into the resource chain
  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
  这一段代码含义晦涩,我们需要对其进行解读:
  (1)上述代码完成MFC扩展DLL的初始化和终止处理;
  (2)初始化期间所创建的 CDynLinkLibrary 对象使MFC扩展 DLL 可以将 DLL中的CRuntimeClass 对象或资源导出到应用程序;
  (3)AfxInitExtensionModule函数捕获模块的CRuntimeClass 结构和在创建 CDynLinkLibrary 对象时使用的对象工厂(COleObjectFactory 对象);
  (4)AfxTermExtensionModule函数使 MFC 得以在每个进程与扩展 DLL 分离时(进程退出或使用AfxFreeLibrary卸载DLL时)清除扩展 DLL;
  (5)第一条语句static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };定义了一个AFX_EXTENSION_MODULE类的静态全局对象,AFX_EXTENSION_MODULE的定义如下:
struct AFX_EXTENSION_MODULE
 HMODULE hM
 HMODULE hR
 CRuntimeClass* pFirstSharedC
 COleObjectFactory* pFirstSharedF
  由AFX_EXTENSION_MODULE的定义我们可以更好的理解(2)、(3)、(4)点。
  在资源编辑器中添加一个如图15所示的对话框,并使用MFC类向导为其添加一个对应的类CExtDialog,系统自动添加了ExtDialog.h和ExtDialog.cpp两个头文件。
图15 MFC扩展DLL中的对话框
  修改ExtDialog.h中CExtDialog类的声明为:
class AFX_EXT_CLASS CExtDialog : public CDialog
  CExtDialog( CWnd* pParent = NULL );
  enum { IDD = IDD_DLL_DIALOG };
 protected:
  virtual void DoDataExchange( CDataExchange* pDX );
  DECLARE_MESSAGE_MAP()
  这其中最主要的改变是我们在class AFX_EXT_CLASS CExtDialog语句中添加了“AFX_EXT_CLASS”宏,则使得DLL中的CExtDialog类被导出。
6.3 MFC扩展DLL的加载
  6.3.1 隐式加载
  我们在6.2工程所在的工作区中添加一个LoadExtDllDlg工程,用于演示MFC扩展DLL的加载。在LoadExtDllDlg工程中添加一个如图16所示的对话框,这个对话框上包括一个“调用DLL”按钮。
图16 MFC扩展DLL调用工程中的对话框
  在与图16对应对话框类实现文件的头部添加:
// LoadExtDllDlg.cpp : implementation file
#include &..\ExtDialog.h&
#pragma comment( lib, &ExtDll.lib& )
而“调用DLL”按钮的单击事件的消息处理函数为:
void CLoadExtDllDlg::OnDllcallButton()
 CExtDialog extD
 extDialog.DoModal();
  当我们单击“调用DLL”的时候,弹出了如图15的对话框。
  为提供给用户隐式加载(MFC扩展DLL一般使用隐式加载,具体原因见下节),MFC扩展DLL需要提供三个文件:
  (1)描述DLL中扩展类的头文件;
  (2)与动态链接库对应的.LIB文件;
  (3)动态链接库.DLL文件本身。
  有了这三个文件,应用程序的开发者才可充分利用MFC扩展DLL。
  6.3.2 显示加载
  显示加载MFC扩展DLL应使用MFC全局函数AfxLoadLibrary而不是WIN32 API中的LoadLibrary。AfxLoadLibrary 最终也调用了 LoadLibrary这个API,但是在调用之前进行了线程同步的处理。
  AfxLoadLibrary 的函数原型与 LoadLibrary完全相同,为:
HINSTANCE AFXAPI AfxLoadLibrary( LPCTSTR lpszModuleName );
  与之相对应的是,MFC 应用程序应使用AfxFreeLibrary 而非FreeLibrary 卸载MFC扩展DLL。AfxFreeLibrary的函数原型也与 FreeLibrary完全相同,为:
BOOL AFXAPI AfxFreeLibrary( HINSTANCE hInstLib );
  如果我们把上例中的“调用DLL”按钮单击事件的消息处理函数改为:
void CLoadExtDllDlg::OnDllcallButton()
 HINSTANCE hDll = AfxLoadLibrary( &ExtDll.dll& );
 if(NULL == hDll)
  AfxMessageBox( &MFC扩展DLL动态加载失败& );
 CExtDialog extD
 extDialog.DoModal();
 AfxFreeLibrary(hDll);
  则工程会出现link错误:
LoadExtDllDlg.obj : error LNK2001: unresolved external symbol &__declspec(dllimport) public: virtual __thiscall CExtDialog::~CExtDialog(void)& (__imp_??1CExtDialog@@UAE@XZ)
LoadExtDllDlg.obj : error LNK2001: unresolved external symbol &__declspec(dllimport) public: __thiscall CExtDialog::CExtDialog(class CWnd *)& (__imp_??0CExtDialog@@QAE@PAVCWnd@@@Z)
  提示CExtDialog的构造函数和析构函数均无法找到!是的,对于派生MFC类的MFC扩展DLL,当我们要在应用程序中使用DLL中定义的派生类时,我们不宜使用动态加载DLL的方法。
  6.4 MFC扩展DLL加载MFC扩展DLL
  我们可以在MFC扩展DLL中再次使用MFC扩展DLL,但是,由于在两个DLL中对于AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA宏的定义都是输出,这会导致调用的时候出现问题。
  我们将会在调用MFC扩展DLL的DLL中看到link错误:
error LNK2001: unresolved external symbol ….......
  因此,在调用MFC扩展DLL的MFC扩展DLL中,在包含被调用DLL的头文件之前,需要临时重新定义AFX_EXT_CLASS的值。下面的例子显示了如何实现:
//临时改变宏的含义“输出”为“输入”
#undef AFX_EXT_CLASS
#undef AFX_EXT_API
#undef AFX_EXT_DATA
#define AFX_EXT_CLASS AFX_CLASS_IMPORT
#define AFX_EXT_API AFX_API_IMPORT
#define AFX_EXT_DATA AFX_DATA_IMPORT
//包含被调用MFC扩展DLL的头文件
#include &CalledDLL.h&
//恢复宏的含义为输出
#undef AFX_EXT_CLASS
#undef AFX_EXT_API
#undef AFX_EXT_DATA
#define AFX_EXT_CLASS AFX_CLASS_EXPORT
#define AFX_EXT_API AFX_API_EXPORT
#define AFX_EXT_DATA AFX_DATA_EXPORT
6.5 MFC扩展DLL导出函数和变量
  MFC扩展DLL导出函数和变量的方法也十分简单,下面我们给出一个简单的例子。
  我们在MFC向导生成的MFC扩展DLL工程中添加gobal.h和global.cpp两个文件:
//global.h:MFC扩展DLL导出变量和函数的声明
extern &C&
 int AFX_EXT_DATA //导出变量
 int AFX_EXT_API add( int x, int y ); //导出函数
//global.cpp:MFC扩展DLL导出变量和函数定义
#include &StdAfx.h&
#include &global.h&
extern &C&
int add(int x,int y)
 total = x +
  编写一个简单的控制台程序来调用这个MFC扩展DLL:
#include &iostream.h&
#include &afxver_.h&
//AFX_EXT_DATA、AFX_EXT_API宏的定义在afxver_.h头文件中
#pragma comment ( lib, &ExtDll.lib& )
#include &..\global.h&
int main(int argc, char* argv[])
 cout && add(2,3) &&
 return 0;
  运行程序,在控制台上看到:
  另外,在Visual C++下建立MFC扩展DLL时,MFC DLL向导会自动生成.def文件。因此,对于函数和变量,我们除了可以利用AFX_EXT_DATA、AFX_EXT_API宏导出以外,在.def文件中定义导出也是一个很好的办法。与之相比,在.def文件中导出类却较麻烦。通常需要从工程生成的.map文件中获得类的所有成员函数被C++编译器更改过的标识符,并且在.def文件中导出这些“奇怪”的标识符。因此,MFC扩展DLL通常以AFX_EXT_CLASS宏直接声明导出类。
  6.6 MFC扩展DLL的应用
  上述各小节所举MFC扩展DLL的例子均只是为了说明某方面的问题,没有真实地体现“MFC扩展” 的内涵,譬如6.2派生自CDialog的类也不具备比CDialog更强的功能。MFC扩展DLL的真实内涵体现在它提供的类虽然派生自MFC类,但是提供了比MFC类更强大的功能、更丰富的接口。下面我们来看一个具体的例子()。
  我们知道static控件所对应的CStatic类不具备设置背景和文本颜色的接口,这使得我们不能在对话框或其它用户界面上自由灵活地修改static控件的颜色风格,因此我们需要一个提供了SetBackColor和SetTextColor接口的CStatic派生类CMultiColorStatic。
  这个类的声明如下:
class AFX_EXT_CLASS CMultiColorStatic : public CStatic
 // Construction
  CMultiColorStatic();
  virtual ~CMultiColorStatic();
  // Attributes
 protected:
  CString m_strC
  COLORREF m_BackC
  COLORREF m_TextC
  // Operations
  void SetTextColor( COLORREF TextColor );
  void SetBackColor( COLORREF BackColor );
  void SetCaption( CString strCaption );
  // Generated message map functions
 protected:
  afx_msg void OnPaint();
  DECLARE_MESSAGE_MAP()
  在这个类的实现文件中,我们需要为它提供WM_PAINT消息的处理函数(这是因为颜色的设置依赖于WM_PAINT消息):
BEGIN_MESSAGE_MAP(CMultiColorStatic, CStatic)
//{{AFX_MSG_MAP(CMultiColorStatic)
 ON_WM_PAINT() //为这个类定义WM_PAINT消息处理函数
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
  下面是这个类中的重要成员函数:
//为CMultiColorStatic类添加“设置文本颜色”接口
void CMultiColorStatic::SetTextColor( COLORREF TextColor )
 m_TextColor = TextC //设置文字颜色
//为CMultiColorStatic类添加“设置背景颜色”接口
void CMultiColorStatic::SetBackColor( COLORREF BackColor )
 m_BackColor = BackC //设置背景颜色
//为CMultiColorStatic类添加“设置标题”接口
void CMultiColorStatic::SetCaption( CString strCaption )
 m_strCaption = strC
//重画Static,颜色和标题的设置都依赖于这个函数
void CMultiColorStatic::OnPaint()
 CPaintDC dc(this); // device context for painting
 GetClientRect( &rect );
 dc.SetBkColor( m_BackColor );
 dc.SetBkMode( TRANSPARENT );
 CFont *pFont = GetParent()-&GetFont();//得到父窗体的字体
 CFont *pOldF
 pOldFont = dc.SelectObject( pFont );//选用父窗体的字体
 dc.SetTextColor( m_TextColor );//设置文本颜色
 dc.DrawText( m_strCaption, &rect, DT_CENTER );//文本在Static中央
 dc.SelectObject( pOldFont );
  为了验证CMultiColorStatic类,我们制作一个基于对话框的应用程序,它包含一个如图17所示的对话框。该对话框上包括一个static控件和三个按钮,这三个按钮可分别把static控件设置为“红色”、“蓝色”和“绿色”。
图17 扩展的CStatic类调用演示
  下面看看应如何编写与这个对话框对应的类。
  包含这种Static的对话框类的声明如下:
#include &..\MultiColorStatic.h&
#pragma comment ( lib, &ColorStatic.lib& )
// CCallDllDlg dialog
class CCallDllDlg : public CDialog
  CCallDllDlg(CWnd* pParent = NULL); // standard constructor
  enum { IDD = IDD_CALLDLL_DIALOG };
  CMultiColorStatic m_ //包含一个CMultiColorStatic的实例
 protected:
  virtual void DoDataExchange(CDataExchange* pDX);//DDX/DDV support
  HICON m_hI
 // Generated message map functions
 //{{AFX_MSG(CCallDllDlg)
 virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 afx_msg void OnRedButton();
 afx_msg void OnBlueButton();
 afx_msg void OnGreenButton();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
  下面是这个类中与使用CMultiColorStatic相关的主要成员函数:
void CCallDllDlg::DoDataExchange(CDataExchange* pDX)
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CCallDllDlg)
  DDX_Control(pDX, IDC_COLOR_STATIC, m_colorstatic);
 //使m_colorstatic与IDC_COLOR_STATIC控件关联
 //}}AFX_DATA_MAP
BOOL CCallDllDlg::OnInitDialog()
 // TODO: Add extra initialization here
 // 初始static控件的显示
 m_colorstatic.SetCaption(&最开始为黑色&);
 m_colorstatic.SetTextColor(RGB(0,0,0));
 return TRUE; // return TRUE unless you set the focus to a control
//设置static控件文本颜色为红色
void CCallDllDlg::OnRedButton()
 m_colorstatic.SetCaption( &改变为红色& );
 m_colorstatic.SetTextColor( RGB( 255, 0, 0 ) );
 Invalidate( TRUE ); //导致发出WM_PAINT消息
//设置static控件文本颜色为蓝色
void CCallDllDlg::OnBlueButton()
 m_colorstatic.SetCaption( &改变为蓝色& );
 m_colorstatic.SetTextColor( RGB( 0, 0, 255 ) );
 Invalidate( TRUE ); //导致发出WM_PAINT消息
//设置static控件文本颜色为绿色
void CCallDllDlg::OnGreenButton()
 m_colorstatic.SetCaption( &改变为绿色& );
 m_colorstatic.SetTextColor( RGB(0,255,0) );
 Invalidate( TRUE ); //导致发出WM_PAINT消息
  至此,我们已经讲解完成了所有类型的动态链接库,即非MFC DLL、MFC规则DLL和MFC扩展DLL。下一节将给出DLL的三个工程实例,与读者朋友们共同体会DLL的应用范围和使用方法。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:363573次
积分:4825
积分:4825
排名:第2840名
原创:17篇
转载:840篇
评论:26条
(11)(17)(9)(25)(13)(34)(9)(2)(6)(12)(4)(20)(25)(36)(24)(18)(9)(6)(13)(3)(12)(4)(11)(3)(1)(4)(3)(4)(9)(5)(5)(20)(5)(5)(14)(10)(45)(81)(117)(7)(3)(23)(20)(105)(45)

我要回帖

更多关于 无法定位xlive.dll 的文章

 

随机推荐