📄 19.3.1 利用 extern声明外部函数.txt
字号:
19.3.1 利用 extern声明外部函数
在 DllTest这个测试程序中,在调用 Dll1. dll中的 add和 subtract函数之前,为了让编译器知道这两个函数,需要对这两个函数作一个声明,声明语句如例 19-3所示(应放在
OnBtnAdd函数和 OnBtnSubtract函数的定义之前),前面加上 extern关键字表明函数是在外部定义的。
例 19-3
extern int add(int a , int b);
extern int subtract(int a , int b);
然后在 OnB tnAdd函数中就可以调用 Dll l. dll导出的 add函数了,代码如例 19-4所示。例 19-4
void CDllTestDlg: :OnBtnAdd()
// TODO : Add your control not工 fi c ation handler code here
CString str;
str .Format( " 5+3=%d " , add ( 5 , 3 ) ) ;
MessageBox(str);
在如例 19-4所示代码中,首先定义了一个 CString对象: str,然后调用 Dll l.dll提供的 add方法计算 "5+3"的结果,并利用 Format方法将该结果格式化到由对象中,最后用 MessageBox函数显示该结果。
同样,在 OnBtnSubtract函数调用 Dl1l. dll导出的 subtract函数,代码如例 19-5所示。
例 19-5
void CDI ITestDlg : :OnBtnSubtract()
{ 11 TODO : Add your control notification handler code here
CString str;
str . Format( " 5 -3=%d ", subtract(5, 3)) ;
MessageBox (str) ;
利用 Build命令生成 DllTest程序,这时将会出现三个错误, VC++的 Build窗口中部分输出信息如下所示:
------------------Conf工 guration: DIITest -W工 n32 Debug------------------
...
Linking. . .
DIITestDlg.obj : error LNK2001: unresolved external symbol "int cdecl add(int , int)" (?add@@YAHHH@Z)
DIITestDlg.obj : error LNK2001: unresolved external symbol "int cdecl subtract(int , int)" (?subtract@@YAHHH@Z)
Debug/DIITest.exe : fatal error LNKl120: 2 unresolved externals
Error executing link.exe.
DllT豆豆 t.. .!3xe -3 error (s) , 0 warning (s)
可以看到, DllTest程序编译成功通过,产生的三个错误是在程序链接时发生的。因为这里调用的 add和 subtract函数都己经作了声明,所以编译可以通过。当链接时,链接器需要知道这两个函数到底是在哪个地方实现的,它要找到这两个函数的实现。正因为没有找到该信息,所以链接时就出错了。
为了解决这个问题,就需要利用动态链接库的引入库文件了。在 Dll l. dll文件所在目录下,复制 Dll 1.lib文件,并将其复制到 DllTest程序所在目录下,这个文件中就包含了 Dll1.dll中导出函数的符号名。然后在 DllTest程序中,选择{Project\Settings}菜单命令,打开工程设置对话框,选择 link选项卡,在 "ObjectJl ibrary modules"选项编辑框中输入: dlll.lib (如图 19.5所示),这与前面介绍 Socket编程时,链接 ws2 32.lib文件的操作是一样的。
再次利用 Build命令生成 DllTest程序,这时将会成功生成 DllTest.exe文件。也就是说,当应用程序需要调用某个动态链接库提供的函数时,在程序链接时只需要包含该动态链接库提供的输入库文件就可以了。前面已经提到,在引入库文件中并没有包含实际的代码,它只是用来为链接程序提供必要的信息,以便在可执行文件中建立动态链接时需要用到的重定位表。我们也可以查看可执行程序的输入信息,以及其加载的 DLL信息,这同样也可以利用 dumpbin命令来实现。在命令行方式下,首先进入 DllTest.exe文件所在目录下,然后输入下述命令并回车:
dumpbin /imports dlltest.exe
图 19.5为DllTest工程添加动态链接库的链接
这时,将输出 DllTest程序的输入信息,如图 19.6所示 (由于输入的内容较多,篇幅有限,这里仅列出了部分信息)。
图 19.6 DllTest辑序使用的DLL信息
从输出的信息可以看到, DllTest程序需要调用Dlll.dll中的两个函数: add和subtract。另外,可以看到,该程序还需要调用MFC42D.dll中的函数,这个动态链接库是在MFC程序在调试版本下需要使用的库。如果是在发行版本下,需要使用MFC42.dll文件。在随后
的信息中,可以发现该程序还需要使用 MSVCRTD.dll,这是 的库包括: Kemal32.dll、 User32.dll和MFC042D.dll。再次运行DllTest.exe程序,但发现程序将会弹出一个如图 提示在指定的路径下无法找到动态链接库Dl1l.dl1文件。 C运行时库。19.7所示的错 其他需要使用误提示对话框,
图 19.7错误提示对话框
前面已经提到,当应用程序运行时,系统将为它分配一个 4GB的地址空间,然后加载模块会分析该应用程序的输入信息,从中找到该程序将要访问的动态链接库信息,然后在用户机器上搜索这些动态链接库,进而加载它们。搜索的顺序,已经在图 19.7所示的对话框中列出来了,依次分别是:
·
程序的执行目录
本例即对话框中提示的: "D:\VC++深入编程\CHARPTER19\Dl1Test'\Debug"。
·
当前目录
对话框提示信息中提示的第二个目录,即一个点:".
·
系统目录
依次是C:\WINNT\system32, C:\WINNT\system, C:\.WINNT
. pa由环境变量中所列出的路径
读者可以发现,加载程序搜索动态链接库的顺序,与前面讲述CreateProcess函数搜索可执行模块的顺序是一样的。为了让可执行文件能够正常运行,就必须让加载模块能够找到该应用程序所需的所有动态链接库,如果加载模块未能找到其中的一个动态链接库,可执行程序就将终止运行。在实际编程时,可以把这些动态链接库放置在加载模块将要搜索的目录中的任一目录下。对本例来说,可以把Dlll.dll放置在DllTest工程所在目录下。然后再次运行 DllTest程序,并分别单击对话框窗口上的【Add】和【Subtract】按钮,将分别弹出如图 19.8和图 19.9所示的对话框,可以看到DllTest程序正确地调用了 Dlll.dl1提供的add和subtract函数。
图19.8单击Add按钮后的结果图19.9单击 Subtract按钮后的结果
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -