动态链接库DLL编程深入浅出3.docx

上传人:b****8 文档编号:10937166 上传时间:2023-02-23 格式:DOCX 页数:16 大小:212.28KB
下载 相关 举报
动态链接库DLL编程深入浅出3.docx_第1页
第1页 / 共16页
动态链接库DLL编程深入浅出3.docx_第2页
第2页 / 共16页
动态链接库DLL编程深入浅出3.docx_第3页
第3页 / 共16页
动态链接库DLL编程深入浅出3.docx_第4页
第4页 / 共16页
动态链接库DLL编程深入浅出3.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

动态链接库DLL编程深入浅出3.docx

《动态链接库DLL编程深入浅出3.docx》由会员分享,可在线阅读,更多相关《动态链接库DLL编程深入浅出3.docx(16页珍藏版)》请在冰豆网上搜索。

动态链接库DLL编程深入浅出3.docx

动态链接库DLL编程深入浅出3

第4节我们对非MFCDLL进行了介绍,这一节将详细地讲述MFC规则DLL的创建与使用技巧。

  另外,自从本文开始连载后,收到了一些读者的e-mail。

有的读者提出了一些问题,笔者将在本文的最后一次连载中选取其中的典型问题进行解答。

由于时间的关系,对于读者朋友的来信,笔者暂时不能一一回复,还望海涵!

由于笔者的水平有限,文中难免有错误和纰漏,也热诚欢迎读者朋友不吝指正!

  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类的派生类。

  RegularDLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。

在这种动态连接库中,包含一个从CWinApp继承下来的类,DllMain函数则由MFC自动提供。

  RegularDLL分为两类:

  

(1)静态链接到MFC的规则DLL

  静态链接到MFC的规则DLL与MFC库(包括MFC扩展DLL)静态链接,将MFC库的代码直接生成在.dll文件中。

在调用这种DLL的接口时,MFC使用DLL的资源。

因此,在静态链接到MFC的规则DLL中不需要进行模块状态的切换。

  使用这种方法生成的规则DLL其程序较大,也可能包含重复的代码。

  

(2)动态链接到MFC的规则DLL

  动态链接到MFC的规则DLL可以和使用它的可执行文件同时动态链接到MFCDLL和任何MFC扩展DLL。

在使用了MFC共享库的时候,默认情况下,MFC使用主应用程序的资源句柄来加载资源模板。

这样,当DLL和应用程序中存在相同ID的资源时(即所谓的资源重复问题),系统可能不能获得正确的资源。

因此,对于共享MFCDLL的规则DLL,我们必须进行模块切换以使得MFC能够找到正确的资源模板。

我们可以在VisualC++中设置MFC规则DLL是静态链接到MFCDLL还是动态链接到MFCDLL。

如图8,依次选择VisualC++的project->Settings->General菜单或选项,在MicrosoftFoundationClasses中进行设置。

图8设置动态/静态链接MFCDLL

  5.2MFC规则DLL的创建

  我们来一步步讲述使用MFC向导创建MFC规则DLL的过程,首先新建一个project,如图9,选择project的类型为MFCAppWizard(dll)。

点击OK进入如图10所示的对话框。

图9MFCDLL工程的创建

  图10所示对话框中的1区选择MFCDLL的类别。

  2区选择是否支持automation(自动化)技术,automation允许用户在一个应用程序中操纵另外一个应用程序或组件。

例如,我们可以在应用程序中利用MicrosoftWord或MicrosoftExcel的工具,而这种使用对用户而言是透明的。

自动化技术可以大大简化和加快应用程序的开发。

  3区选择是否支持WindowsSockets,当选择此项目时,应用程序能在TCP/IP网络上进行通信。

CWinApp派生类的InitInstance成员函数会初始化通讯端的支持,同时工程中的StdAfx.h文件会自动include头文件。

添加socket通讯支持后的InitInstance成员函数如下:

BOOLCRegularDllSocketApp:

:

InitInstance()

{

if(!

AfxSocketInit())

{

AfxMessageBox(IDP_SOCKETS_INIT_FAILED);

returnFALSE;

}

returnTRUE;

}

  4区选择是否由MFC向导自动在源代码中添加注释,一般我们选择“Yes,please”。

图10MFCDLL的创建选项

  5.3一个简单的MFC规则DLL

  这个DLL的例子(属于静态链接到MFC的规则DLL)中提供了一个如图11所示的对话框。

图11MFC规则DLL例子

  在DLL中添加对话框的方式与在MFC应用程序中是一样的。

  在图11所示DLL中的对话框的Hello按钮上点击时将MessageBox一个“Hello,pconline的网友”对话框,下面是相关的文件及源代码,其中删除了MFC向导自动生成的绝大多数注释(下载本工程附件):

第一组文件:

CWinApp继承类的声明与实现

//RegularDll.h:

mainheaderfilefortheREGULARDLLDLL

#if!

defined(AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_)

#defineAFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_

#if_MSC_VER>1000

#pragmaonce

#endif//_MSC_VER>1000

#ifndef__AFXWIN_H__

#errorinclude'stdafx.h'beforeincludingthisfileforPCH

#endif

#include"resource.h"//mainsymbols

classCRegularDllApp:

publicCWinApp

{

public:

CRegularDllApp();

DECLARE_MESSAGE_MAP()

};

#endif

//RegularDll.cpp:

DefinestheinitializationroutinesfortheDLL.

#include"stdafx.h"

#include"RegularDll.h"

#ifdef_DEBUG

#definenewDEBUG_NEW

#undefTHIS_FILE

staticcharTHIS_FILE[]=__FILE__;

#endif

BEGIN_MESSAGE_MAP(CRegularDllApp,CWinApp)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

//CRegularDllAppconstruction

CRegularDllApp:

:

CRegularDllApp()

{

}

/////////////////////////////////////////////////////////////////////////////

//TheoneandonlyCRegularDllAppobject

CRegularDllApptheApp;

  分析:

  在这一组文件中定义了一个继承自CWinApp的类CRegularDllApp,并同时定义了其的一个实例theApp。

乍一看,您会以为它是一个MFC应用程序,因为MFC应用程序也包含这样的在工程名后添加“App”组成类名的类(并继承自CWinApp类),也定义了这个类的一个全局实例theApp。

我们知道,在MFC应用程序中CWinApp取代了SDK程序中WinMain的地位,SDK程序WinMain所完成的工作由CWinApp的三个函数完成:

virtualBOOLInitApplication();

virtualBOOLInitInstance();

virtualBOOLRun();//传说中MFC程序的“活水源头”

  但是MFC规则DLL并不是MFC应用程序,它所继承自CWinApp的类不包含消息循环。

这是因为,MFC规则DLL不包含CWinApp:

:

Run机制,主消息泵仍然由应用程序拥有。

如果DLL生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从DLL导出的函数来调用PreTranslateMessage成员函数。

  另外,MFC规则DLL与MFC应用程序中一样,需要将所有DLL中元素的初始化放到InitInstance成员函数中。

  第二组文件自定义对话框类声明及实现(点击查看附件)

  分析:

  这一部分的编程与一般的应用程序根本没有什么不同,我们照样可以利用MFC类向导来自动为对话框上的控件添加事件。

MFC类向导照样会生成类似ON_BN_CLICKED(IDC_HELLO_BUTTON,OnHelloButton)的消息映射宏。

  第三组文件DLL中的资源文件

//{{NO_DEPENDENCIES}}

//MicrosoftDeveloperStudiogeneratedincludefile.

//UsedbyRegularDll.rc

//

#defineIDD_DLL_DIALOG1000

#defineIDC_HELLO_BUTTON1000

  分析:

  在MFC规则DLL中使用资源也与在MFC应用程序中使用资源没有什么不同,我们照样可以用VisualC++的资源编辑工具进行资源的添加、删除和属性的更改。

  第四组文件MFC规则DLL接口函数

#include"StdAfx.h"

#include"DllDialog.h"

extern"C"__declspec(dllexport)voidShowDlg(void)

{

CDllDialogdllDialog;

dllDialog.DoModal();

}

  分析:

  这个接口并不使用MFC,但是在其中却可以调用MFC扩展类CdllDialog的函数,这体现了“规则”的概类。

  与非MFCDLL完全相同,我们可以使用__declspec(dllexport)声明或在.def中引出的方式导出MFC规则DLL中的接口。

5.4MFC规则DLL的调用

  笔者编写了如图12的对话框MFC程序(下载本工程附件)来调用5.3节的MFC规则DLL,在这个程序的对话框上点击“调用DLL”按钮时弹出5.3节MFC规则DLL中的对话框。

图12MFC规则DLL的调用例子

  下面是“调用DLL”按钮单击事件的消息处理函数:

voidCRegularDllCallDlg:

:

OnCalldllButton()

{

typedefvoid(*lpFun)(void);

HINSTANCEhDll;//DLL句柄

hDll=LoadLibrary("RegularDll.dll");

if(NULL==hDll)

{

MessageBox("DLL加载失败");

}

lpFunaddFun;//函数指针

lpFunpShowDlg=(lpFun)GetProcAddress(hDll,"ShowDlg");

if(NULL==pShowDlg)

{

MessageBox("DLL中函数寻找失败");

}

pShowDlg();

}

上述例子中给出的是显示调用的方式,可以看出,其调用方式与第4节中非MFCDLL的调用方式没有什么不同。

我们照样可以在EXE程序中隐式调用MFC规则DLL,只需要将DLL工程生成的.lib文件和.dll文件拷入当前工程所在的目录,并在RegularDllCallDlg.cpp文件(图12所示对话框类的实现文件)的顶部添加:

#pragmacomment(lib,"RegularDll.lib")

voidShowDlg(void);

  并将voidCRegularDllCallDlg:

:

OnCalldllButton()改为:

voidCRegularDllCallDlg:

:

OnCalldllButton()

{

ShowDlg();

}

  5.5共享MFCDLL的规则DLL的模块切换

  应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。

进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。

如果程序同时加载了多个DLL,则每个DLL模块都会有不同的HINSTANCE。

应用程序在加载DLL时对其进行了重定位。

  共享MFCDLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。

EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。

如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。

  这次我们创建一个动态链接到MFCDLL的规则DLL(下载本工程附件),在其中包含如图13的对话框。

图13DLL中的对话框

  另外,在与这个DLL相同的工作区中生成一个基于对话框的MFC程序,其对话框与图12完全一样。

但是在此工程中我们另外添加了一个如图14的对话框。

图14EXE中的对话框

  图13和图14中的对话框除了caption不同(以示区别)以外,其它的都相同。

尤其值得特别注意,在DLL和EXE中我们对图13和图14的对话框使用了相同的资源ID=2000,在DLL和EXE工程的resource.h中分别有如下的宏:

//DLL中对话框的ID

#defineIDD_DLL_DIALOG2000

//EXE中对话框的ID

#defineIDD_EXE_DIALOG2000

  与5.3节静态链接MFCDLL的规则DLL相同,我们还是在规则DLL中定义接口函数ShowDlg,原型如下:

#include"StdAfx.h"

#include"SharedDll.h"

voidShowDlg(void)

{

CDialogdlg(IDD_DLL_DIALOG);//打开ID为2000的对话框

dlg.DoModal();

}

  而为应用工程主对话框的“调用DLL”的单击事件添加如下消息处理函数:

voidCSharedDllCallDlg:

:

OnCalldllButton()

{

ShowDlg();

}

  我们以为单击“调用DLL”会弹出如图13所示DLL中的对话框,可是可怕的事情发生了,我们看到是图14所示EXE中的对话框!

  惊讶?

  产生这个问题的根源在于应用程序与MFC规则DLL共享MFCDLL(或MFC扩展DLL)的程序总是默认使用EXE的资源,我们必须进行资源模块句柄的切换,其实现方法有三:

  方法一在DLL接口函数中使用:

AFX_MANAGE_STATE(AfxGetStaticModuleState());

我们将DLL中的接口函数ShowDlg改为:

voidShowDlg(void)

{

//方法1:

在函数开始处变更,在函数结束时恢复

//将AFX_MANAGE_STATE(AfxGetStaticModuleState());作为接口函数的第一//条语句进行模块状态切换

AFX_MANAGE_STATE(AfxGetStaticModuleState());

CDialogdlg(IDD_DLL_DIALOG);//打开ID为2000的对话框

dlg.DoModal();

}

  这次我们再点击EXE程序中的“调用DLL”按钮,弹出的是DLL中的如图13的对话框!

嘿嘿,弹出了正确的对话框资源。

AfxGetStaticModuleState是一个函数,其原型为:

AFX_MODULE_STATE*AFXAPIAfxGetStaticModuleState();

  该函数的功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类(模块全局数据也就是模块状态)的实例,对其进行设置,并将其指针pModuleState返回。

AFX_MODULE_STATE类的原型如下:

//AFX_MODULE_STATE(globaldataforamodule)

classAFX_MODULE_STATE:

publicCNoTrackObject

{

public:

#ifdef_AFXDLL

AFX_MODULE_STATE(BOOLbDLL,WNDPROCpfnAfxWndProc,DWORDdwVersion);

AFX_MODULE_STATE(BOOLbDLL,WNDPROCpfnAfxWndProc,DWORDdwVersion,BOOLbSystem);

#else

AFX_MODULE_STATE(BOOLbDLL);

#endif

~AFX_MODULE_STATE();

CWinApp*m_pCurrentWinApp;

HINSTANCEm_hCurrentInstanceHandle;

HINSTANCEm_hCurrentResourceHandle;

LPCTSTRm_lpszCurrentAppName;

…//省略后面的部分

}

  AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作,类似汇编中call指令对pc指针和sp寄存器的保存与恢复、中断服务程序的中断现场压栈与恢复以及操作系统线程调度的任务控制块保存与恢复。

  许多看似不着边际的知识点居然有惊人的相似!

AFX_MANAGE_STATE是一个宏,其原型为:

AFX_MANAGE_STATE(AFX_MODULE_STATE*pModuleState)

  该宏用于将pModuleState设置为当前的有效模块状态。

当离开该宏的作用域时(也就离开了pModuleState所指向栈上对象的作用域),先前的模块状态将由AFX_MODULE_STATE的析构函数恢复。

  方法二在DLL接口函数中使用:

AfxGetResourceHandle();

AfxSetResourceHandle(HINSTANCExxx);

  AfxGetResourceHandle用于获取当前资源模块句柄,而AfxSetResourceHandle则用于设置程序目前要使用的资源模块句柄。

  我们将DLL中的接口函数ShowDlg改为:

voidShowDlg(void)

{

//方法2的状态变更

HINSTANCEsave_hInstance=AfxGetResourceHandle();

AfxSetResourceHandle(theApp.m_hInstance);

CDialogdlg(IDD_DLL_DIALOG);//打开ID为2000的对话框

dlg.DoModal();

//方法2的状态还原

AfxSetResourceHandle(save_hInstance);

}

  通过AfxGetResourceHandle和AfxSetResourceHandle的合理变更,我们能够灵活地设置程序的资源模块句柄,而方法一则只能在DLL接口函数退出的时候才会恢复模块句柄。

方法二则不同,如果将ShowDlg改为:

externCSharedDllApptheApp;//需要声明theApp外部全局变量

voidShowDlg(void)

{

//方法2的状态变更

HINSTANCEsave_hInstance=AfxGetResourceHandle();

AfxSetResourceHandle(theApp.m_hInstance);

CDialogdlg(IDD_DLL_DIALOG);//打开ID为2000的对话框

dlg.DoModal();

//方法2的状态还原

AfxSetResourceHandle(save_hInstance);

//使用方法2后在此处再进行操作针对的将是应用程序的资源

CDialogdlg1(IDD_DLL_DIALOG);//打开ID为2000的对话框

dlg1.DoModal();

}

  在应用程序主对话框的“调用DLL”按钮上点击,将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图14)。

方法三由应用程序自身切换

  资源模块的切换除了可以由DLL接口函数完成以外,由应用程序自身也能完成(下载本工程附件)。

  现在我们把DLL中的接口函数改为最简单的:

voidShowDlg(void)

{

CDialogdlg(IDD_DLL_DIALOG);//打开ID为2000的对话框

dlg.DoModal();

}

  而将应用程序的OnCalldllButton函数改为:

voidCSharedDllCallDlg:

:

OnCalldllButton()

{

//方法3:

由应用程序本身进行状态切换

//获取EXE模块句柄

HINSTANCEexe_hInstance=GetModuleHandle(NULL);

//或者HINSTANCEexe_hInstance=AfxGetResourceHandle();

//获取DLL模块句柄

HINSTANCEdll_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进行详细分析和实例讲解,欢迎您继续关注本系列连载。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 法律文书 > 调解书

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1