📄 chap13.html
字号:
<P ALIGN="JUSTIFY">如果现在运行该程序,将出现如图</FONT><FONT SIZE=3>13.1</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>所示的对话框。</P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1><P ALIGN="CENTER"><IMG SRC="Image447.gif" tppabs="http://166.111.167.223/computer/cai/visual_c++_5.0_programming/Image447.gif" WIDTH=321 HEIGHT=89></P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1><P ALIGN="CENTER">图</FONT><FONT SIZE=1>13.1 </FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1>未找到</FONT><FONT SIZE=1>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1>时出现的错误</P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">上面的对话框说明程序没有在指定的路径未找到所需要的</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY">一般情况下,程序在运行时,系统将按如下的顺序查找程序所使用的动态链接库:</P>
<UL>
<P ALIGN="JUSTIFY"><LI>系统预安装的</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>,如</FONT><FONT SIZE=3>KERNEL32.DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>和</FONT><FONT SIZE=3>USER32.DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>等</LI></P>
<P ALIGN="JUSTIFY"><LI>当前目录</LI></P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY"><LI>Windows</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的系统的目录,如</FONT><FONT SIZE=3>WINNT\system32</LI></P>
<P ALIGN="JUSTIFY"><LI>Windows</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>所在的目录,如</FONT><FONT SIZE=3>WINNT</LI></P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"><LI>环境变量</FONT><FONT SIZE=3>PATH</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中所指定的目录</LI></P></UL>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1><P ALIGN="CENTER"><IMG SRC="Image448.gif" tppabs="http://166.111.167.223/computer/cai/visual_c++_5.0_programming/Image448.gif" WIDTH=190 HEIGHT=62></P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1><P ALIGN="CENTER">图</FONT><FONT SIZE=1>13.2 tester</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1>应用程序的运行结果</P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">如果</FONT><FONT SIZE=3>Windows</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>在上面的目录中未找到所需要的</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>,则弹出如图</FONT><FONT SIZE=3>13.1</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>所示的对话框。这里,我们把</FONT><FONT SIZE=3>msgbox.dll</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>文件拷贝到</FONT><FONT SIZE=3>tester\Debug</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>目录下,再运行应用程序,则出现如图</FONT><FONT SIZE=3>13.2</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>所示的对话框。</P><DIR>
</FONT><FONT FACE="Arial" SIZE=3><P> (2)	</FONT><FONT FACE="黑体" LANG="ZH-CN" SIZE=3>使用显式链接</P></DIR>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">如果没有与</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>相关联的</FONT><FONT SIZE=3>LIB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>文件,则必须使用显式链接。使用显式链接同样必须知道函数返回值的类型和所传递的参数个数、类型和顺序。与使用隐含链接不同的是,使用显式链接的应用程序在调用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的导出函数前,必须使用</FONT><FONT SIZE=3>LoadLibrary()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数加载</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>并得到一个模块句柄。然后使用该句柄调用</FONT><FONT SIZE=3>GetProcAddress()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数获得所需调用的导出函数的指针,并通过该指针调用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的导出函数,这种模式使用显式链接到</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的应用程序不再需要相应的</FONT><FONT SIZE=3>LIB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>文件。在使用完毕之后,还需调用</FONT><FONT SIZE=3>FreeLibrary()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数释放加载的</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY"></P>
<P ALIGN="JUSTIFY">下面我们使用显式链接的方式来实现前面的例子。</P>
<P ALIGN="JUSTIFY">由于使用指针来调用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的导出函数,所以本例中不再需要</FONT><FONT SIZE=3>msgbox.h</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>文件。</P>
<P ALIGN="JUSTIFY">在</FONT><FONT SIZE=3>tester.cpp</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中添加的代码如下所示:</P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1><P>#include <windows.h></P>
<P>typedef int (CALLBACK* DLLFUNC)(</P>
<P>	LPCTSTR lpText="虽然这个例子有一些简单,但它工作得非常的好!",</P>
<P>	LPCTSTR lpCaption="一个简单的例子",</P>
<P>	UINT=MB_OK);</P>
<P>int WINAPI WinMain(HINSTANCE hInstance,</P>
<P>				 HINSTANCE hPrevInstance,</P>
<P>				 LPSTR lpCmdLine,</P>
<P>				 int nCmdShow)</P>
<P>{</P>
<P>	HINSTANCE hDLL;</P>
<P>	DLLFUNC MsgBox;</P>
<P>	hDLL = LoadLibrary("msgbox");</P>
<P>	if (hDLL != NULL)</P>
<P>	{</P>
<P>		MsgBox = </P>
<P>		 (DLLFUNC)GetProcAddress(hDLL,"MsgBox");</P>
<P>		return MsgBox();</P>
<P>	}</P>
<P>}</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">LoadLibrary()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数的参数是所调用的</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的名字,这个名字不是放入输入库文件中的名字,而是</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的文件名。如果文件的扩展名为</FONT><FONT SIZE=3>.DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>,则可以省略。</P>
<P ALIGN="JUSTIFY"></P>
<P ALIGN="JUSTIFY">这个程序的运行结果同使用隐含链接的前一个程序一样,但它的内部实现是很不相同的。使用显式链接的应用程序加载时,所调用的</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>并不加载,只有当应用程序调用</FONT><FONT SIZE=3>LoadLibray()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>时系统才加载相应的</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>,并在应用程序调用</FONT><FONT SIZE=3>FreeLibrary()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>时卸载该</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>。使用隐含链接的应用程序调用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的导出函数时,方法同调用一般的函数一样,而使用显式链接的应用程序必须使用指针来调用。由于使用了指针,因此在编译时不能验证参数的合法性,通过指针使用不合法的参数来调用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的导出函数将会导致不可预料的后果。</P>
<P ALIGN="JUSTIFY">很明显,使用隐含链接的方式调用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的导出函数要比使用显式链接方便得多。但在某些情况下我们必须使用显式链接。事实上,使用显式链接调用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>提供了更大的灵活性。尤其在没有与</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>相对应的</FONT><FONT SIZE=3>LIB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>文件时,我们只能使用显式链接来调用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的导出函数,并且,只要我们使用函数名作参数来调用</FONT><FONT SIZE=3>GetProcAddress()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>,在更新</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>时,就没有必要重新链接应用程序。另外,使用隐含链接的方式的应用程序加载</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>时如果发生错误(如</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>文件未找到或是</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的</FONT><FONT SIZE=3>DllMain()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数初始化失败)时,应用程序将被终止,而使用显式链接的应用程序则可以使用如上面的例子中所给出的方法来避免出现这种情况(可以使用所创建的两个不同版本的</FONT><FONT SIZE=3>tester</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>程序来验证这一点)。</P>
<P ALIGN="JUSTIFY">由于应用程序调用</FONT><FONT SIZE=3>LoadLibrary()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数时才加载</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>,因此使用显式链接的应用程序的加载速度要比使用隐含链接的应用程序快。使用显式链接的另一个好处是,应用程序可以在运行时决定所加载的</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY">但是要记住,由于使用了指针来传递应用程序的参数,因此编译器在编译时无法确认应用程序所传递的参数类型是否合法。传递不合法的参数给</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的导出函数的一件危险的事。在程序调试的过程中我们一定需要注意这一点。</P>
</FONT><FONT FACE="仿宋_GB2312" LANG="ZH-CN" SIZE=4><P ALIGN="CENTER"><A NAME="_Toc425699308"><A NAME="_Toc425699516">第三节</FONT><FONT SIZE=4> </FONT><FONT FACE="仿宋_GB2312" LANG="ZH-CN" SIZE=4>使用动态链接连库扩展</FONT><FONT SIZE=4>MFC</A></A></P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">我们还可以使用</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>来实现从</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>派生的一些可重用类,这种动态链接库一般称作</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>扩展动态链接库</FONT><FONT SIZE=3>(MFC Extension DLL)</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>。正如这个名称所暗示的那样,通过这种方式我们可以扩展</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>所包括的内容,使得使用</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>编程更加的方便。此外,如果需要在应用程序和</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>之间传递</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>或者由</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>派生的对象的指针的话,我们也必须使用</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>扩展</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY">在本节中,我们使用</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>扩展</FONT><FONT SIZE=3>DLL</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>来创建一个输入通用对话框,如图</FONT><FONT SIZE=3>13.3</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>所示。该对话框很象</FONT><FONT SIZE=3>Visual Basic</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中的</FONT><FONT SIZE=3>InputBox</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数所产生的对话框,使用过</FONT><FONT SIZE=3>Visual Basic</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的程序员都有印象,函数</FONT><FONT SIZE=3>InputBox</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>非常之好用,这里,我们来使用动态链接库在</FONT><FONT SIZE=3>Visual C++</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中也创建这么一个好用的类。</P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1><P ALIGN="CENTER"><IMG SRC="Image449.gif" tppabs="http://166.111.167.223/computer/cai/visual_c++_5.0_programming/Image449.gif" WIDTH=184 HEIGHT=74></P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1><P ALIGN="CENTER">图</FONT><FONT SIZE=1>13. 3 </FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1>输入通用对话框</P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">输入通用对话框由类</FONT><FONT SIZE=3>CInputDlg</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>封装,类</FONT><FONT SIZE=3>CInputDlg</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>提供了一个公有成员函数</FONT><FONT SIZE=3>GetInput</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>,该成员函数的原型如下:</P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=1><P>CString GetInput(CString Title, CString Prompt)</P>
</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">第一个参数</FONT><FONT SIZE=3>Title</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>表示输入对话框的标题,在图</FONT><FONT SIZE=3>13.3</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>中为“输入”;第二个参数</FONT><FONT SIZE=3>Prompt</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZ
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -