友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

C语言实例教程(PDF格式)-第56部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!



一样,只不过在函数MsgBox()中,不需要指定消息的父窗口,而且可 

以缺省其它所有的参数。  



   (1) 使用模块定义文件  



模块定义文件是一个文本文件,它包括了一系列的模块语句,这些语 

句用来描述DLL的各种属性,典型的,模块语句定义了DLL中所导出的 

函数的名称和顺序值。  



在讲解模块定义文件之前,我们先创建一个Win32                   Dynamic…Link  

Library工程。  



1。 在Microsoft Developer Studio中选择File菜单下的New命令,在 

Projects选项卡中选择Win32 Dynamic…Link Library,并为工程取一 

个名字,如msgbox。单击OK后,Visual  C++创建一个Win32  DLL的空 

白工程,必须手动的将所需要的文件添加到工程中。  



2。  单击Project菜单下的Add  To  Project子菜单下的New命令,在 

Files选项卡中选择Text   File,在File文本框中输入DEF文件名,如 

msgbox。def。  



3。   双击Workspace窗口的FileView选项卡中的msgbox。def节点,在 

msgbox。def文件中输入下面的内容:  



LIBRARY MSGBOX  



DESCRIPTION 〃一个DLL的简单例子〃  



EXPORTS  



MsgBox @1  



在DEF文件中的第一条语句必须是LIBRARY语句,该语句表明该DEF文 

件属于一个DLL,在LIBRARY之后是DLL的名称,这个名称在链接时将 

放到DLL的引入库中。  



EXPORTS语句下列出了DLL的所有导出函数以及它们的顺序值。函数的 

顺序值不是必须的,在指定导出函数的顺序值时,我们在函数名后跟 


…………………………………………………………Page 667……………………………………………………………

上一个@符号和一个数字,该数字即导出函数的顺序值。如果在DEF中 

指定了顺序值,它必须不小于1,且不大于DLL中所有导出函数的数 

目。  



DESCRIPTION语句是可选的,它简单的说明了DLL的用途。  



4。  下一步是向工程中添加一个头文件,它定义了DLL中的函数的返回 

值的类型和参数的个数、顺序和类型。  



单击菜单项Project|Add To Project|New。。。,在Files选项卡下选择 

C/C++   Header   File,在File文本框中指定头文件名,如msgbox。h 

 (可以省略后缀名。h)。  



在头文件中输入如下的内容:  



#include   



extern 〃C〃 int MsgBox(  



// 消息框的文本  



LPCTSTR lpText=〃虽然这个例子有一些幼稚,但它工作得非常的好 !〃;  



// 消息框的标题  



LPCTSTR lpCaption=〃一个简单的例子〃;  



// 消息框的样式  



UINT uType=MB_OK);  



请注意函数定义前的关键字extern   〃C〃,这是由于我们使用了C++语 

言来开发DLL,为了使C语言模块能够访问该导出函数,我们应该使用 

C链接来代替C++链接。否则,C++编译器将使用C++的类型安全命名和 

调用协议,这在使用C调用该函数时就会遇上问题。在本例中并不需 

要考虑到这个问题,因为我们在开发DLL和应用程序时都是使用C++, 

但我们仍然强烈建议使用extern   〃C〃,以保证在使用C编写的程序调 

用该DLL的导出函数不会遇上麻烦,在本章后面的内容中我们还会讨 

论到这个问题。  



5。  下面要做的事是向工程中添加一个C++源文件,在该文件中实现函 

数MsgBox()。  



仿照上面的过程,单击菜单项Project|Add  To  Project|New。。。,在 

Files选项卡下选择C++  Source  File,在File文本框中指定源文件 


…………………………………………………………Page 668……………………………………………………………

名,如msgbox。cpp。  



在msgbox。cpp文件中添加如下的代码:  



#include 〃test1。h〃  



int MsgBox(LPCTSTR lpText;  



LPCTSTR lpCaption;  



UINT uType)  



{  



return MessageBox(NULL;lpText;lpCaption;uType);  



}  



编译该工程,在Debug 目录下生成文件msgbox。lib和msgbox。dll。  



在 “13。2。2  链接应用程序到DLL”小节中将讲述如何使用在本节中所 

创建的DLL:msgbox。dll。  



     (2) 使用关键字__declspec(dllexport)  



从DLL中导出文件的另一种方法是在定义函数时使用__declspec 

(dllexport)关键字。这种方法不需要使用DEF文件。  



仍使用前面的例子,在工程中删除msgbox。def文件,将msgbox。h文件 

修改如下:  



#include   



extern 〃C〃 __declspec(dllexport) int MsgBox(  



// 消息框的文本  



LPCTSTR lpText=〃虽然这个例子有一些幼稚,但它工作得非常的好 !〃;  



// 消息框的标题  



LPCTSTR lpCaption=〃一个简单的例子〃;  



// 消息框的样式  



UINT uType=MB_OK);  



msgbox。cpp文件并不需要做任何修改,重新编译该工程,在Debug 目 


…………………………………………………………Page 669……………………………………………………………

录下仍生成两个文件msgbox。lib和msgbox。dll。  



在下一小节 “13。2。2  链接应用程序到DLL”中讲述了如何在应用程序 

中使用所创建的DLL:msgbox。dll的导出函数MsgBox()。  



使用__declspec(dllexport)从DLL中导出类的语法如下:  



class __declspec(dllexport) CDemoClass  



{  



。。。  



}  



    



  l 注意:  



  l 如果在使用__declspec(dllexport)的同时指定了调用协议关键 

    字,则必须将__declspec(dllexport)关键字放在调用协议关键字 

    的左边。如:  



  l int __declspec(dllexport) __cdacl MyFunc();  



  l 在32位版本的Visual C++中,__declspec(dllexport)和 

   __declspec(dllimport)代替了16版本中使用的__export关键字。 

    因此,在将16位的DLL源代码移植到Win32平台时,需要把每一处 

   __export替换为__declsped(dllexport)。  



如何从这两种导出函数的方法中作出选择,可以从下面的几个方面考 

虑:  



  l 如果需要使用导出顺序值 (export ordinal value),那么应该 

   使用DEF文件来导出函数。只在使用DEF文件导出函数才能指定导 

    出函数的顺序值。使用顺序值的一个好处是当向DLL中添加新的函 

    数时,只要新的导出函数的顺序值大于原有的导出函数,就没有 

    必要重新链接使用隐含链接的应用程序。相反,如果使用 

   __declspec(dllexport)来导出函数,如果向DLL中添加了新的函 

    数,使用隐含链接的应用程序有可以需要重新编译和链接。  



  l 使用DEF文件来导出函数,可以创建具有NONAME属性的DLL。具有 

   NONAME属性的DLL在导出表中仅包含了导出函数的顺序值,这种类 

    型的DLL在包括有大量的导出函数时,其文件长度要小于通常的 


…………………………………………………………Page 670……………………………………………………………

   DLL。  



  l 使用DEF文件从C++文件导出函数,应该在定义函数时使用extern  

   〃C〃或者在DEF文件中指定导出函数的decorated name。否则,由 

   于编译器所产生的decorated name是基于特定编译器的,链接到 

   该DLL的应用程序也必须使用创建DLL的同一版本的Visual C++来 

   编译和链接。  



  l 由于使用__declspec(dllexport)关键字导出函数不需要编写DEF 

   文件,因此,如果编写的DLL只供自己使用,使用__declspec 

   (dllexport)较为简单。  



    



  l 注意:  



  l MFC本身使用了DEF文件从MFCx0。DLL中导出函数和类。  



13。2。2 链接应用程序到DLL  



同样,链接应用程序到DLL也有两种方法:  



  l 隐含链接  



  l 显式链接  



隐含链接有时又称为静态加载。如果应用程序使用了隐含链接,操作 

系统在加载应用程序的同时加载应用程序所使用的DLL。显式链接有 

时又称为动态加载。使用动态加载的应用程序必须在代码中明确的加 

载所使用的DLL,并使用指针来调用DLL中的导出函数,在使用完毕之 

后,应用程序必须卸载所使用的DLL。  



同一个DLL可以被应用程序隐含链接,也可以被显式链接,这取决于 

应用程序的目的和实现。  



下面我们在分别讲述两种不同的链接方式之后再作对比。  



   (1) 使用隐含链接  



在使用隐含链接除了需要相应的DLL文件外,还必须具备如下的条 

件:  



  l 一个包括导出的函数或C++类的头文件  


…………………………………………………………Page 671……………………………………………………………

  l 一个输入库文件 (。LIB文件)  



通常情况下,我们需要从DLL的提供者那里得到上面的文件。输入库 

文件是在DLL文件被链接时由链接程序生成的。  



在 “13。2。1   DLL的结构和导出方式”中所创建的DLL:msgbox。dll所 

对应的头文件msgbox。h如下:  



#include   



extern 〃C〃 __declspec(dllimport) int MsgBox(  



// 消息框的文本  



LPCTSTR lpText=〃虽然这个例子有一些幼稚,但它工作得非常的好 !〃;  



// 消息框的标题  



LPCTSTR lpCaption=〃一个简单的例子〃;  



// 消息框的样式  



UINT uType=MB_OK);  



需要注意的是,这个msgbox。h文件和创建DLL时所使用msgbox。h是不 

同的,唯一的差别在于,创建DLL时的msgbox。h中使用的是 

__declspec(dllexport)关键字,而供应用程序所使用的msgbox。h 中 

使用的是__declspec(dllimport)关键字。无论创建DLL时使用的是 

DEF文件还是__declspec(dllexport)关键字,均可使用__declspec 

(dllimport)关键字从DLL中引入函数。引入函数时也可以省略 

__declspec(dllimport)关键字,但是使用它可以使编译器生成效率 

更高的代码。  



  l 注意:  



  l 如果需要引入的是DLL中的公用数据和对象,则必须使用 

    __declspec(dllimport)关键字。  



现在使用Microsoft  Developer  Studio创建一个Win32  Application 

工程,命名为tester。向工程中添加一个C++源文件,如 

tester。cpp。在tester。cpp文件中输入下面的代码:  



#include 〃msgbox。h〃 // 应将msgbox。h文件拷贝到工程tester的目录下。  



int WINAPI WinMain(HINSTANCE hInstance;  


…………………………………………………………Page 672……………………………………………………………

HINSTANCE hPrevInstance;  



LPSTR lpCmdLine;  



int nCmdShow)  



{  



return MsgBox();  



}  



在上面的代码中,MsgBox()函数的所有参数都使用了缺省值。  



  l 注意:  



  l 在编译之前,将上一步生成的msgbox。lib文件拷贝到tester工程 

    所在的目录下。然后单击菜单项Project|Settings。。。,将 

    msgbox。lib添加到Link选项卡下的Object/library modules文本 

    框中。如果忽略这一步,链接时将会导致错误。完成之后创建该 

    应用程序。  



如果现在运行该程序,将出现如图13。1所示的对话框。  



                                                 



                     图13。1 未找到DLL时出现的错误  



上面的对话框说明程序没有在指定的路径未找到所需要的DLL。  



一般情况下,程序在运行时,系统将按如下的顺序查找程序所使用的 

动态链接库:  



  l 系统预安装的DLL,如KERNEL32。DLL和USER32。DLL等  



  l 当前目录  



  l Windows的系统的目录,如WINNTsystem32  



  l Windows所在的目录,如WINNT  



  l 环境变量PATH中所指定的目录  


…………………………………………………………Page 673……………………………………………………………

                                            



                    图13。2 tester应用程序的运行结果  



如果Windows在上面的目录中未找到所需要的DLL,则弹出如图13。1所 

示的对话框。这里,我们把msgbox。dll文件拷贝到testerDebug 目录 

下,再运行应用程序,则出现如图13。2所示的对话框。  



    (2) 使用显式链接  



如果没有与DLL相关联的LIB文件,则必须使用显式链接。使用显式链 

接同样必须知道函数返回值的类型和所传递的参数个数、类型和顺 

序。与使用隐含链接不同的是,使用显式链接的应用程序在调用DLL 

中的导出函数前,必须使用LoadLibrary()函数加载DLL并得到一个模 

块句柄。然后使用该句柄调用GetProcAddress()函数获得所需调用的 

导出函数的指针,并通过该指针调用DLL中的导出函数,这种模式使 

用显式链接到DLL的应用程序不再需要相应的LIB文件。在使用完毕之 

后,还需调用FreeLibrary()函数释放加载的DLL。  



下面我们使用显式链接的方式来实现前面的例子。  



由于使用指针来调用DLL中的导出函数,所以本例中不再需要 

msgbox。h文件。  



在tester。cpp中添加的代码如下所示:  



#include   



typedef int (CALLBACK* DLLFUNC)(  



LPCTSTR lpText=〃虽然这个例子有一些简单,但它工作得非常的好 !〃;  



LPCTSTR lpCaption=〃一个简单的例子〃;  



UINT=MB_OK);  



int WINAPI WinMain(HINSTANCE hInstance;  



HINSTANCE hPrevInstance;  



LPSTR lpCmdLine;  



int nCmdShow)  



{  


…………………………………………………………Page 674……………………………………………………………

HINSTANCE hDLL;  



DLLFUNC MsgBox;  



hDLL = LoadLibrary(〃msgbox〃);  



if (hDLL != NULL)  



{  



MsgBox =   



(DLLFUNC)GetProcAddress(hDLL;〃MsgBox〃);  



return MsgBox();  



}  



}  



LoadLibrary()函数的参数是所调用的DLL的名字,这个名字不是放入 

输入库文件中的名字,而是DLL的文件名。如果文件的扩展名 

为。DLL,则可以省略。  



这个程序的运行结果同使用隐含链接的前一个程序一样,但它的内部 

实现是很不相同的。使用显式链接的应用程序加载时,所调用的DLL 

并不加载,只有当应用程序调用LoadLibray()时系统才加载相应的 

DLL,并在应用程序调用FreeLibrary()时卸载该DLL。使用隐含链接 

的应用程序调用DLL中的导出函数时,方法同调用一般的函数一样, 

而使用显式链接的应用程序必须使用指针来调用。由于使用了指针, 

因此在编译时不能验证参数的合法性,通过指针使用不合法的参数来 

调用DLL中的导出函数将会导致不可预料的后果。  



很明显,使用隐含链接的方式调用DLL中的导出函数要比使用显式链 

接方便得多。但在某些情况下我们必须使用显式链接。事实上,使用 

显式链接调用DLL提供了更大的灵活性。尤其在没有与DLL相对应的 

LIB文件时,我们只能使用显式链接来调用DLL中的导出函数,并且, 

只要我们使用函数名作参数来调用GetProcAddress(),在更新DLL 

时,就没有必要重新链接应用程序。另外,使用隐含链接的方式的应 

用程序加载DLL时如果发生错误 (如DLL文件未找到或是DLL中的 

DllMain()函数初始化失败)时,应用程序将被终止,而使用显式链 

接的应用程序则可以使用如上面的例子中所给出的方法来避免出现这 

种情况 (可以使用所创建的两个不同版本的tester程序来验证这一 

点)。  


…………………………………………………………Page 675……………………………………………………………

由于应用程序调用LoadLibrary()函数时才加载DLL,因此使用显式链 

接的应用程序的加载速度要比使用隐含链接的应用程序快。使用显式 

链接的另一个好处是,应用程序可以在运行时决定所加载的DLL。  



但是要记住,由于使用了指针来传递应用程序的参数,因此编译器在 

编译时无法确认应用程序所传递的参数类型是否合法。传递不合法的 

参数给DLL中的导出函数的一件危险的事。在程序调试的过程中我们 

一定需要注意这一点。  



          第三节 使用动态链接连库扩展MFC  



我们还可以使用DLL来实现从MFC派生的一些可重用类,这种动态链接 

库一般称作MFC扩展动态链接库 (MFC Extension DLL)。正如这个名称 

所暗示的那样,通过这种方式我们可以扩展MFC所包括的内容,使得 

使用MFC编程更加的方便。此外,如果需要在应用程序和DLL之间传递 

MFC或者由MFC派生的对象的指针的话,我们也必须使用MFC扩展DLL。  



在本节中,我们使用MFC扩展DLL来创建一个输入通用对话框,如图 

13。3所示。该对话框很象Visual  Basic中的InputBox函数所产生的对 

话框,使用过Visual  Basic的程序员都有印象,函数InputBox非常之 

好用,这里,我们来使用动态链接库在Visual   C++ 的MFC中也创建这 

么一个好用的类。  



                               



                 图13。 3 输入通用对话框  



输入通用对话框由类CInputDlg封装,类CInputDlg提供了一个公有成 

员函数GetInput,该成员函数的原型如下:  



CString GetInput(CString Title; CString Prompt)  



第一个参数Title表示输入对话框的标题,在图13。3中为 “输入”; 

第二个参数Prompt代表在输入对话框中显示的简短提示文本,在图 

13。3中为 “请输入对话框的标题:”。函数的返回值为用户在对话框 

的文本框中输入的字符串。如果用户没有输入任何字符串或者单击了 

 “取消”按钮,返回值为空串〃〃。  



下面我们来介绍该对话框的创建和使用。首先讲述DLL工程 

ExtDllDemo的创建。该工程实现了类CInputDlg的导出。  


…………………………………………………………Page 676……………………………………………………………

1。             使用AppWizard创建一个MFC扩展DLL工程,将工程取名为 

ExtDllDemo。  



2。  向工程中添加一个对话框资源IDD_INPUT,按图13。3绘制对话框中 

的各控件。这些控件的资源ID如表所示。  



                   表13。 1 对话框资源IDD_INPUT中的控件属性  



                           控件          资源ID  



                           提示文本        IDC_PROMPT  

                           标签  



                           输入文本        IDC_EDIT  

                           框  



3。         使用ClassWizard为对话框资源IDD_INPUT创建新的对话框类 

CInputDlg,该类直接派生于CDialog。按下面的代码修改类 

CInputDlg的头文件和实现文件。  



#if !defined(AFX_INPUTDLG_H__02DB98CF_1F76_11D2_971A_0000B4810A31__INCLUDED_)  



#define AFX_INPUTDLG_H__02DB98CF_1F76_11D2_971A_0000B4810A31__INCLUDED_  



#if _MSC_VER 》= 1000  



#pragma once  



#endif // _MSC_VER 》= 1000  



// InputDlg。h : 头文件  



//  



// 以下对话框 ID 常量需要手动添加  



#define IDC_EDIT 1000  



#define IDC_PR
返回目录 上一页 下一页 回到顶部 9 9
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!