📄 vc++动态链接库(dll)编程深入浅出(三).htm
字号:
href="http://www.study888.com/User/User_Favorite.asp?Action=Add&ChannelID=25&InfoID=125869"
target=_blank>加入收藏</A>】【<A
href="http://www.study888.com/computer/SendMail.asp?ArticleID=125869"
target=_blank>告诉好友</A>】【<A
href="http://www.study888.com/computer/Print.asp?ArticleID=125869"
target=_blank>打印此文</A>】【<A
href="javascript:window.close();">关闭窗口</A>】 </TD></TR><!--文章标题下部广告代码开始-->
<TR>
<TD align=middle colSpan=2>
<SCRIPT src=""></SCRIPT>
</TD></TR><!--文章标题下部广告代码结束-->
<TR>
<TD background=VC++动态链接库(DLL)编程深入浅出(三).files/ad_bx1.gif colSpan=2
height=6></TD></TR>
<TR>
<TD colSpan=6></TD></TR>
<TR>
<TD id=fontzoom style="WORD-BREAK: break-all" vAlign=top colSpan=2
height=600>
<SCRIPT src="VC++动态链接库(DLL)编程深入浅出(三).files/wen-ad-300.js"></SCRIPT>
第4节我们对非MFC
DLL进行了介绍,这一节将详细地讲述MFC规则DLL的创建与使用技巧。<BR><BR> 另外,自从本文开始连载后,收到了一些读者的e-mail。有的读者提出了一些问题,笔者将在本文的最后一次连载中选取其中的典型问题进行解答。由于时间的关系,对于读者朋友的来信,笔者暂时不能一一回复,还望海涵!由于笔者的水平有限,文中难免有错误和纰漏,也热诚欢迎读者朋友不吝指正!<BR><BR><BR> <STRONG>5.
MFC规则DLL</STRONG><BR><BR> <STRONG>5.1
概述</STRONG><BR><BR> MFC规则DLL的概念体现在两方面:<BR><BR> (1)
它是MFC的<BR><BR> “是MFC的”意味着可以在这种DLL的内部使用MFC;<BR><BR> (2)
它是规则的<BR><BR> “是规则的”意味着它不同于MFC扩展DLL,在MFC规则DLL的内部虽然可以使用MFC,但是其与应用程序的接口不能是MFC。而MFC扩展DLL与应用程序的接口可以是MFC,可以从MFC扩展DLL中导出一个MFC类的派生类。<BR><BR> Regular
DLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。在这种动态连接库中,包含一个从CWinApp继承下来的类,DllMain函数则由MFC自动提供。<BR><BR> Regular
DLL分为两类:<BR><BR> (1)静态链接到MFC
的规则DLL<BR><BR> 静态链接到MFC的规则DLL与MFC库(包括MFC扩展
DLL)静态链接,将MFC库的代码直接生成在.dll文件中。在调用这种DLL的接口时,MFC使用DLL的资源。因此,在静态链接到MFC
的规则DLL中不需要进行模块状态的切换。<BR><BR> 使用这种方法生成的规则DLL其程序较大,也可能包含重复的代码。<BR><BR> (2)动态链接到MFC
的规则DLL<BR><BR> 动态链接到MFC 的规则DLL 可以和使用它的可执行文件同时动态链接到 MFC DLL 和任何MFC扩展
DLL。在使用了MFC共享库的时候,默认情况下,MFC使用主应用程序的资源句柄来加载资源模板。这样,当DLL和应用程序中存在相同ID的资源时(即所谓的资源重复问题),系统可能不能获得正确的资源。因此,对于共享MFC
DLL的规则DLL,我们必须进行模块切换以使得MFC能够找到正确的资源模板。<BR>我们可以在Visual
C++中设置MFC规则DLL是静态链接到MFC DLL还是动态链接到MFC DLL。如图8,依次选择Visual C++的project
-> Settings -> General菜单或选项,在Microsoft Foundation
Classes中进行设置。<BR>
<P></P>
<P align=center><IMG alt=""
src="VC++动态链接库(DLL)编程深入浅出(三).files/20051121084943348.jpg"
border=0></P>
<P align=center>图8 设置动态/静态链接MFC DLL</P>
<P> <STRONG>5.2
MFC规则DLL的创建</STRONG><BR><BR> 我们来一步步讲述使用MFC向导创建MFC规则DLL的过程,首先新建一个project,如图9,选择project的类型为MFC
AppWizard(dll)。点击OK进入如图10所示的对话框。<BR></P>
<P align=center><IMG alt=""
src="VC++动态链接库(DLL)编程深入浅出(三).files/20051121084944136.jpg"
border=0></P>
<P align=center>图9 MFC DLL工程的创建</P>
<P> 图10所示对话框中的1区选择MFC DLL的类别。<BR><BR> 2区选择是否支持automation(自动化)技术,
automation 允许用户在一个应用程序中操纵另外一个应用程序或组件。例如,我们可以在应用程序中利用 Microsoft Word
或Microsoft
Excel的工具,而这种使用对用户而言是透明的。自动化技术可以大大简化和加快应用程序的开发。<BR><BR> 3区选择是否支持Windows
Sockets,当选择此项目时,应用程序能在 TCP/IP 网络上进行通信。
CWinApp派生类的InitInstance成员函数会初始化通讯端的支持,同时工程中的StdAfx.h文件会自动include
<AfxSock.h>头文件。<BR>添加socket通讯支持后的InitInstance成员函数如下:<BR></P>
<P></P>
<P class=code>BOOL
CRegularDllSocketApp::InitInstance()<BR><BR>{<BR><BR>if
(!AfxSocketInit())<BR><BR>{<BR><BR>AfxMessageBox(IDP_SOCKETS_INIT_FAILED);<BR><BR>return
FALSE;<BR><BR>}<BR><BR><BR>return TRUE;<BR><BR>}</P>
<P><BR> 4区选择是否由MFC向导自动在源代码中添加注释,一般我们选择“Yes,please”。<BR></P>
<P align=center><IMG alt=""
src="VC++动态链接库(DLL)编程深入浅出(三).files/20051121084944504.jpg"
border=0></P>
<P align=center>图10 MFC DLL的创建选项</P>
<P> <STRONG>5.3 一个简单的MFC规则DLL</STRONG><BR><BR> 这个DLL的例子(属于静态链接到MFC
的规则DLL)中提供了一个如图11所示的对话框。<BR></P>
<P align=center><IMG alt=""
src="VC++动态链接库(DLL)编程深入浅出(三).files/20051121084944732.jpg"
border=0></P>
<P align=center>图11 MFC规则DLL例子</P>
<P> 在DLL中添加对话框的方式与在MFC应用程序中是一样的。<BR><BR> 在图11所示DLL中的对话框的Hello按钮上点击时将MessageBox一个“Hello,pconline的网友”对话框,下面是相关的文件及源代码,其中删除了MFC向导自动生成的绝大多数注释</P>
<P>第一组文件:CWinApp继承类的声明与实现<BR></P>
<P></P>
<P class=code>// RegularDll.h : main header file for the REGULARDLL
DLL<BR><BR><BR><BR>#if
!defined(AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_)<BR><BR>#define
AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_<BR><BR><BR><BR>#if
_MSC_VER > 1000<BR><BR>#pragma once<BR><BR>#endif // _MSC_VER
> 1000<BR><BR><BR><BR>#ifndef __AFXWIN_H__<BR><BR>#error include
'stdafx.h' before including this file for
PCH<BR><BR>#endif<BR><BR>#include "resource.h" // main
symbols<BR><BR><BR><BR>class CRegularDllApp : public
CWinApp<BR><BR>{<BR><BR>public:<BR><BR>CRegularDllApp();<BR><BR><BR><BR>DECLARE_MESSAGE_MAP()<BR><BR>};<BR><BR>#endif
<BR><BR><BR><BR>// RegularDll.cpp : Defines the initialization
routines for the DLL.<BR><BR><BR><BR>#include
"stdafx.h"<BR><BR>#include "RegularDll.h"<BR><BR><BR><BR>#ifdef
_DEBUG<BR><BR>#define new DEBUG_NEW<BR><BR>#undef
THIS_FILE<BR><BR>static char THIS_FILE[] =
__FILE__;<BR><BR>#endif<BR><BR><BR><BR>BEGIN_MESSAGE_MAP(CRegularDllApp,
CWinApp)<BR><BR>END_MESSAGE_MAP()<BR><BR><BR><BR>/////////////////////////////////////////////////////////////////////////////<BR><BR>//
CRegularDllApp
construction<BR><BR><BR><BR>CRegularDllApp::CRegularDllApp()<BR><BR>{<BR><BR>}<BR><BR><BR><BR>/////////////////////////////////////////////////////////////////////////////<BR><BR>//
The one and only CRegularDllApp object<BR><BR>CRegularDllApp
theApp;</P>
<P><BR> 分析:<BR><BR> 在这一组文件中定义了一个继承自CWinApp的类CRegularDllApp,并同时定义了其的一个实例theApp。乍一看,您会以为它是一个MFC应用程序,因为MFC应用程序也包含这样的在工程名后添加“App”组成类名的类(并继承自CWinApp类),也定义了这个类的一个全局实例theApp。<BR>我们知道,在MFC应用程序中CWinApp取代了SDK程序中WinMain的地位,SDK程序WinMain所完成的工作由CWinApp的三个函数完成:<BR></P>
<P class=code>virtual BOOL InitApplication( );<BR><BR>virtual BOOL
InitInstance( );<BR><BR>virtual BOOL Run( ); //传说中MFC程序的“活水源头”</P>
<P><BR> 但是MFC规则DLL并不是MFC应用程序,它所继承自CWinApp的类不包含消息循环。这是因为,MFC规则DLL不包含CWinApp::Run机制,主消息泵仍然由应用程序拥有。如果DLL
生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从DLL
导出的函数来调用PreTranslateMessage成员函数。<BR><BR> 另外,MFC规则DLL与MFC
应用程序中一样,需要将所有 DLL中元素的初始化放到InitInstance 成员函数中。<BR><BR> 第二组文件
自定义对话框类声明及实现(点击查看<A
href="http://www.pconline.com.cn/pcedu/empolder/gj/vc/0509/acc/05-09-22-vc-1.txt"><U><FONT
color=#0000ff>附件</FONT></U></A>)<BR><BR> 分析:<BR><BR> 这一部分的编程与一般的应用程序根本没有什么不同,我们照样可以利用MFC类向导来自动为对话框上的控件添加事件。MFC类向导照样会生成类似ON_BN_CLICKED(IDC_HELLO_BUTTON,
OnHelloButton)的消息映射宏。<BR><BR> 第三组文件 DLL中的资源文件<BR></P>
<P class=code>//{{NO_DEPENDENCIES}}<BR><BR>// Microsoft Developer
Studio generated include file.<BR><BR>// Used by
RegularDll.rc<BR><BR>//<BR><BR>#define IDD_DLL_DIALOG
1000<BR><BR>#define IDC_HELLO_BUTTON 1000</P>
<P><BR> 分析:<BR><BR> 在MFC规则DLL中使用资源也与在MFC应用程序中使用资源没有什么不同,我们照样可以用Visual
C++的资源编辑工具进行资源的添加、删除和属性的更改。<BR><BR> 第四组文件 MFC规则DLL接口函数<BR></P>
<P class=code>#include "StdAfx.h"<BR><BR>#include
"DllDialog.h"<BR><BR><BR>extern "C" __declspec(dllexport) void
ShowDlg(void) <BR><BR>{<BR><BR>CDllDialog
dllDialog;<BR><BR>dllDialog.DoModal();<BR><BR>}</P>
<P><BR> 分析:<BR><BR> 这个接口并不使用MFC,但是在其中却可以调用MFC扩展类CdllDialog的函数,这体现了“规则”的概类。<BR><BR> 与非MFC
DLL完全相同,我们可以使用__declspec(dllexport)声明或在.def中引出的方式导出MFC规则DLL中的接口。<BR><STRONG>5.4
MFC规则DLL的调用<BR><BR></STRONG> 笔者编写了如图12的对话框MFC程序(下载本工程<A
href="http://www.pconline.com.cn/pcedu/empolder/gj/vc/0509/acc/RegularDllCall.zip"><U><FONT
color=#0000ff>附件</FONT></U></A>)来调用5.3节的MFC规则DLL,在这个程序的对话框上点击“调用DLL”按钮时弹出5.3节MFC规则DLL中的对话框。<BR></P>
<P align=center><IMG alt=""
src="VC++动态链接库(DLL)编程深入浅出(三).files/20051121084944904.jpg"
border=0></P>
<P align=center>图12 MFC规则DLL的调用例子</P>
<P> 下面是“调用DLL”按钮单击事件的消息处理函数:<BR></P>
<P class=code>void CRegularDllCallDlg::OnCalldllButton()
<BR><BR>{<BR><BR>typedef void (*lpFun)(void);<BR><BR><BR>HINSTANCE
hDll; //DLL句柄 <BR><BR>hDll =
LoadLibrary("RegularDll.dll");<BR><BR>if
(NULL==hDll)<BR><BR>{<BR><BR>MessageBox("DLL加载失败");<BR><BR>}<BR><BR><BR><BR>lpFun
addFun; //函数指针<BR><BR>lpFun pShowDlg =
(lpFun)GetProcAddress(hDll,"ShowDlg");<BR><BR>if
(NULL==pShowDlg)<BR><BR>{<BR><BR>MessageBox("DLL中函数寻找失败");
<BR><BR>}<BR><BR>pShowDlg();<BR><BR>}</P>
<P><BR> 上述例子中给出的是显示调用的方式,可以看出,其调用方式与第4节中非MFC
DLL的调用方式没有什么不同。<BR>我们照样可以在EXE程序中隐式调用MFC规则DLL,只需要将DLL工程生成的.lib文件和.dll文件拷入当前工程所在的目录,并在RegularDllCallDlg.cpp文件(图12所示对话框类的实现文件)的顶部添加:<BR></P>
<P class=code>#pragma comment(lib,"RegularDll.lib")<BR><BR>void
ShowDlg(void); </P>
<P><BR> 并将void CRegularDllCallDlg::OnCalldllButton() 改为:<BR></P>
<P class=code>void CRegularDllCallDlg::OnCalldllButton()
<BR><BR>{<BR><BR>ShowDlg();<BR><BR>}</P>
<P><BR><BR> <STRONG>5.5 共享MFC
DLL的规则DLL的模块切换</STRONG><BR><BR> 应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的HINSTANCE。应用程序在加载DLL时对其进行了重定位。
<BR><BR> 共享MFC
DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。<BR><BR> 这次我们创建一个动态链接到MFC
DLL的规则DLL(下载本工程<A
href="http://www.pconline.com.cn/pcedu/empolder/gj/vc/0509/acc/SharedDll.zip"><U><FONT
color=#0000ff>附件</FONT></U></A>),在其中包含如图13的对话框。<BR></P>
<P align=center><IMG alt=""
src="VC++动态链接库(DLL)编程深入浅出(三).files/20051121084944747.jpg"
border=0></P>
<P align=center>图13 DLL中的对话框</P>
<P> 另外,在与这个DLL相同的工作区中生成一个基于对话框的MFC程序,其对话框与图12完全一样。但是在此工程中我们另外添加了一个如图14的对话框。<BR></P>
<P align=center><IMG alt=""
src="VC++动态链接库(DLL)编程深入浅出(三).files/20051121084944202.jpg"
border=0></P>
<P align=center>图14 EXE中的对话框</P>
<P> 图13和图14中的对话框除了caption不同(以示区别)以外,其它的都相同。<BR>尤其值得特别注意,在DLL和EXE中我们对图13和图14的对话框使用了相同的资源ID=2000,在DLL和EXE工程的resource.h中分别有如下的宏:<BR></P>
<P></P>
<P class=code>//DLL中对话框的ID<BR><BR>#define IDD_DLL_DIALOG
2000<BR><BR><BR>//EXE中对话框的ID<BR><BR>#define IDD_EXE_DIALOG 2000</P>
<P><BR> 与5.3节静态链接MFC
DLL的规则DLL相同,我们还是在规则DLL中定义接口函数ShowDlg,原型如下:<BR></P>
<P class=code>#include "StdAfx.h"<BR><BR>#include
"SharedDll.h"<BR><BR>void ShowDlg(void)<BR><BR>{ <BR><BR>CDialog
dlg(IDD_DLL_DIALOG);
//打开ID为2000的对话框<BR><BR>dlg.DoModal();<BR><BR>}</P>
<P><BR> 而为应用工程主对话框的“调用DLL”的单击事件添加如下消息处理函数:<BR></P>
<P class=code>void CSharedDllCallDlg::OnCalldllButton()
<BR><BR>{<BR><BR>ShowDlg();<BR><BR>}</P>
<P><BR> 我们以为单击“调用DLL”会弹出如图13所示DLL中的对话框,可是可怕的事情发生了,我们看到是图14所示EXE中的对话框!<BR><BR> <STRONG>惊讶?</STRONG><BR><BR> 产生这个问题的根源在于应用程序与MFC规则DLL共享MFC
DLL(或MFC扩展DLL)的程序总是默认使用EXE的资源,我们必须进行资源模块句柄的切换,其实现方法有三:<BR><BR> 方法一
在DLL接口函数中使用:<BR></P>
<P class=code>AFX_MANAGE_STATE(AfxGetStaticModuleState());</P>
<P>我们将DLL中的接口函数ShowDlg改为:<BR></P>
<P class=code>void ShowDlg(void)<BR><BR>{
<BR><BR>//方法1:在函数开始处变更,在函数结束时恢复<BR><BR>//将AFX_MANAGE_STATE(AfxGetStaticModuleState());作为接口函数的第一//条语句进行模块状态切换<BR><BR>AFX_MANAGE_STATE(AfxGetStaticModuleState());<BR><BR><BR>CDialog
dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框<BR><BR>dlg.DoModal();<BR><BR>}</P>
<P><BR> 这次我们再点击EXE程序中的“调用DLL”按钮,弹出的是DLL中的如图13的对话框!嘿嘿,弹出了正确的对话框资源。<BR><BR>AfxGetStaticModuleState是一个函数,其原型为:<BR></P>
<P class=code>AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState(
);</P>
<P><BR> 该函数的功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类(模块全局数据也就是模块状态)的实例,对其进行设置,并将其指针pModuleState返回。<BR><BR>AFX_MODULE_STATE类的原型如下:<BR></P>
<P class=code>// AFX_MODULE_STATE (global data for a
module)<BR><BR>class AFX_MODULE_STATE : public
CNoTrackObject<BR><BR>{<BR><BR>public:<BR><BR>#ifdef
_AFXDLL<BR><BR>AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc,
DWORD dwVersion);<BR><BR>AFX_MODULE_STATE(BOOL bDLL, WNDPROC
pfnAfxWndProc, DWORD dwVersion,BOOL
bSystem);<BR><BR>#else<BR><BR>AFX_MODULE_STATE(BOOL
bDLL);<BR><BR>#endif<BR><BR>~AFX_MODULE_STATE();<BR><BR><BR>CWinApp*
m_pCurrentWinApp;<BR><BR>HINSTANCE
m_hCurrentInstanceHandle;<BR><BR>HINSTANCE
m_hCurrentResourceHandle;<BR><BR>LPCTSTR
m_lpszCurrentAppName;<BR><BR><BR>… //省略后面的部分<BR><BR>}</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -