⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 19.7 显示加载方式加载dll.txt

📁 网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读
💻 TXT
字号:
19.7 显示加载方式加载DLL 
' 
19.7.1 LoadLibrary函数
上面的例子都是通过隐式链接加载方式来实现对动态链接库的访问,下面将采用动态加载方式来访问动态链接库。首先将最新的Dll2.dll文件复制到DllTest工程所在目录下。然后在DllTest工程中,将DllTestDlg.cpp文件中包含Dlll.h文件的那行代码注释起来,并在该工程设置对话框的link选项卡上,删除对Dll1.lib文件的链接。
使用动态方式加载动态链接库时,需要用到 LoadLibrary函数。该函数的作用是将指定的可执行模块映射到调用进程的地址空间。 LoadLibrary函数的原型声明如下所示: 
HMODULE LoadLibrary( LPCTSTR lpFileName); 
LoadLibrary函数不仅能够加载 DLL Cdll) ,还可以加载可执行模块(.exe)。一般来 
724 I ~胁'

说,当加载可执行模块时,主要是为了访问该模块内的一些资源,例如对话框资源、位图资源或图标资源等。 LoadLibrary函数有一个字符串类型 (LPCTSTR)的参数,该参数指定了可执行模块的名称,既可以一个 .dll文件,也可以是一个 .exe文件。如果调用成功, Lo adLibrary函数将返回所加载的那个模块的句柄。该函数的返回类型是 HMODULE。 HMODULE类型和 HINSTANCE类型可以通用。
当获取到动态链接库模块的句柄后,接下来就要想办法获取该动态链接库中导出函数的地址,这可以通过调用 GetProcAddress函数来实现。该函数用来获取 DLL导出函数的地址,其原型声明如下所示: 
FARPROC GetProcAddress( HMODULE hModu1e, LPCSTR 1pProcName); 
可以看到, GetProcAddress函数有两个参数,其含义分别如下所述: 
. hModule 
指定动态链接库模块的句柄,即 LoadLibrary函数的返回值。 
. lpProcName 
一个指向常量的字符指针,指定 DLL导出函数的名字或函数的序号。这里,读者应注意,如果该参数指定的是导出函数的序号,那么该序号必须在低位字中,高位字必须是 0。
如果调用成功, GetProcAddress函数将返回指定导出函数的地址:否则返回 NULL。
下面,我们就利用 DUTest程序动态加载 DU2.dll井访问它提供的导出函数。先将 DllTest程序中调用 Dll 1. dll提供的函数的代码注释起来,然后,我们在 Add按钮鼠标单击命令响应函数中实现动态加载 Dll2.dll,并访问其导出的 add函数以完成加法操作。这时 OnB tnAdd函数的实现如例 19-21所示。
例 19-21

void CDllTestDlg: :OnBtnAdd() 
1. 11 TODO : Add your control notification handler code here 

2. HINSTANCE hInst; 

3. hInst=LoadLibrary ("Dll2.dll"); 11动态加载 DLL 

4. typedef int (*ADDPROC) (int a , int b); 11定义函数指针类型 

5. ADDPROC Add= (ADDPROC) GetProcAddress (hInst , "add") ; 11获取 DLL的导出函数 


6. if( ! Add) 

7. { 


8. Mes sageBox ( "获取函数地址失败!"); 
9. return; 
 10. } 
11. CString str; 
12. str.Format("5+3=%d" , Add ( 5 , 3 ) ) ; 
13. MessageBox(str); 
在如例 19-21所示代码中,首先定义了一个实例句柄对象: hInst,然后调用 LoadLibr町函数加载 Dll 2.dll。接着利用 typedef定义了一个函数指针类型: ADDPROC,它所表示的函数有两个 int类型的参数,并且该函数的返回类型也是 int类型。在程序中之所以定义函
数指针类型,主要是为了在需要时可以用来产生一个函数指针变量,用来接收通过 GetProcAddress函数所返回的函数地址。读者一定要注意,这里的 ADDPROC是一个函数指针类型,而不是一个变量。在如例 19-21所示代码中,接着利用该类型定义了一个函数指针变量: Add,并将 GetProcAddress函数的返回值赋给该变量。但是,因为 GetProcAddress函数返回的类型是 FARPROC,而这里需要的是 ADDPROC类型,所以需要进行一个强制转换。紧接着,程序判断 Add变量是否有值,即判断 GetProcAddress函数是否得到了 DLL中导出函数: add的地址。如果该变量为 NULL,说明 GetProcAddress函数没有得到该导出函数的地址,于是就剂用 MessageBox函数弹出一个消息框提示用户:获取函数地址失败!。然后程序直接返回。如果得到了 DLL中导出函数: add的指针,那么就可以通过 Add变量调用 Dll2提供的导出函数 add了。 
Build并运行 Dl1Test程序,单击【 Add】按钮,即可发现该程序确实调用了 Dll2.dl1导出的 add函数实现加法功能。通过本例可以看出,动态加载 DLL时,客户端程序不再需要包含导出函数声明的头文件和引入库文件,只需要 .dll文件即可。
通过以上的例子,我们可以看到,动态加载和隐式链接这两种加载 DLL的方式各有优点,如果采用动态加载方式,那么可以在需要时才加载 DLL,而隐式链接方式实现起来比较简单,在编写客户端代码时就可以把链接工作做好,在程序中可以随时调用 DLL导出的函数。但是,如果程序需要访问十多个 DLL,如果都采用隐式链接方式加载它们的话,那么在该程序启动时,这些 DLL都需要被加载到内存中,并映射到调用进程的地址空间,这样将加大程序的启动时间。而且,一般来说,在程序运行过程中只是在某个条件满足时才需要访问某个 DLL中的某个函数,其他情况下都不需要访问这些 DLL中的函数。但是这时所有的 DLL都己经被加载到内存中,资源浪费是比较严重的。在这种情况下,就可以采用动态加载的方式访问 DLL,在需要时才加载所需的 DLL,也就是说,在需要时 DLL才会被加载到内存中,并被映射到调用进程的地址空间中。有一点需要说明的是,实际上,采用隐式链接方式访问 DLL时,在程序启动时也是通过调用 LoadLibrary函数加载该进程需要的动态链接库的。
另外,当采用动态方式加载 DLL时,在客户端程序中将不能看到调用该 DLL的输入信息了。读者可以利用 Dumpbin命令的 impons选项查看这时的 Dl1Test. exe的输入信息,将会发现,这时在 DllTest.exe中没有找到调用 Dll2 .dll的信息。 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -