动态链接库

动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。

1.  DLL入口点

在创建 DLL 时,可以有选择地指定入口点函数。当进程或线程将它们自身附加到 DLL 或者将它们自身从 DLL 分离时,将调用入口点函数。您可以使用入口点函数根据 DLL 的需要来初始化数据结构或者销毁数据结构。此外,如果应用程序是多线程的,则可以在入口点函数中使用线程本地存储 (TLS) 来分配各个线程专用的内存。

2.  导出 DLL 函数

要导出 DLL 函数,您可以向导出的 DLL 函数中添加函数关键字,也可以创建模块定义文件 (.def) 以列出导出的 DLL 函数。

l  方法一、向导出的 DLL 函数中添加函数关键字

  要使用函数关键字,您必须使用以下关键字来声明要导出的各个函数: __declspec(dllexport)

  要在应用程序中使用导出的 DLL 函数,您必须使用以下关键字来声明要导入的各个函数:__declspec(dllimport)

  通常情况下,您最好使用一个包含 define 语句和 ifdef 语句的头文件,以便分隔导出语句和导入语句。

l  创建模块定义文件 (.def) 以列出导出的 DLL 函数

使用模块定义文件来声明导出的 DLL 函数。当您使用模块定义文件时,您不必向导出的 DLL 函数中添加函数关键字。在模块定义文件中,您可以声明 DLL 的 LIBRARY 语句和 EXPORTS 语句。

l  特别调用

  关于特定情况下的调用,比如DLL函数中使用到了win32 API或者将C++生成的DLL供标准C语言使用,则需要注意以下一些情况:

  如果使用到了win32 API,则应该使用调用方式为“__stdcall”。

  在将C++生成的DLL供标准C语言使用,输出文件需要用“extern"C"”修饰,否则不能被标准C语言调用。如果使用“__stdcall”调用方式,可能产生C不识别的修饰名,所以设置导出函数时要采用.def文件形式,而不是__declspec(dllexport)形式。后者会进行修饰名转换,C语言无法识别函数。

3.  VC动态链接库的分类

  Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFCExtension DLL(MFC扩展DLL)。

  非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。

4.  共享MFC DLL的规则DLL的模块切换

共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。

产生这个问题的根源在于应用程序与MFC规则DLL共享MFC DLL(或MFC扩展DLL)的程序总是默认使用EXE的资源,我们必须进行资源模块句柄的切换,其实现方法有三:

 方法一 在DLL接口函数中使用:

void ShowDlg(void)

{

 //方法1:在函数开始处变更,在函数结束时恢复

 //将AFX_MANAGE_STATE(AfxGetStaticModuleState());作为接口函数的第一//条语句进行模块状态切换

 AFX_MANAGE_STATE(AfxGetStaticModuleState());

 CDialogdlg(IDD_DLL_DIALOG);//打开ID为2000的对话框

 dlg.DoModal();

}

  方法二 在DLL接口函数中使用:

  AfxGetResourceHandle用于获取当前资源模块句柄,而AfxSetResourceHandle则用于设置程序目前要使用的资源模块句柄。

void ShowDlg(void)

{

 HINSTANCE save_hInstance= AfxGetResourceHandle();//方法2的状态变更

 AfxSetResourceHandle(theApp.m_hInstance);

 CDialogdlg(IDD_DLL_DIALOG);//打开ID为2000的对话框

 dlg.DoModal();

 AfxSetResourceHandle(save_hInstance);//方法2的状态还原

}

  通过AfxGetResourceHandle和AfxSetResourceHandle的合理变更,我们能够灵活地设置程序的资源模块句柄,而方法一则只能在DLL接口函数退出的时候才会恢复模块句柄。

方法三 由应用程序自身切换

  资源模块的切换除了可以由DLL接口函数完成以外,由应用程序自身也能完成。

现在我们把DLL中的接口函数改为最简单的:

void CSharedDllCallDlg::OnCalldllButton()

{

 //方法3:由应用程序本身进行状态切换

 HINSTANCE exe_hInstance =GetModuleHandle(NULL); //获取EXE模块句柄

 //或者HINSTANCE exe_hInstance = AfxGetResourceHandle();

 HINSTANCE dll_hInstance =GetModuleHandle("SharedDll.dll"); //获取DLL模块句柄

 AfxSetResourceHandle(dll_hInstance);//切换状态

 ShowDlg(); //此时显示的是DLL的对话框

 AfxSetResourceHandle(exe_hInstance);//恢复状态

 //资源模块恢复后再调用ShowDlg

 ShowDlg(); //此时显示的是EXE的对话框

}

  方法三中的Win32函数GetModuleHandle可以根据DLL的文件名获取DLL的模块句柄。如果需要得到EXE模块的句柄,则应调用带有Null参数的GetModuleHandle。

  方法三与方法二的不同在于方法三是在应用程序中利用AfxGetResourceHandle和AfxSetResourceHandle进行资源模块句柄切换的。

5.  加载方式

l  隐式加载

#include "..\ExtDialog.h"

#pragma comment( lib,"ExtDll.lib" )

为提供给用户隐式加载,DLL需要提供三个文件:(1)描述DLL中扩展类的头文件;(2)与动态链接库对应的.LIB文件;(3)动态链接库.DLL文件本身。

l  显示加载

HINSTANCE hInst;

hInst=LoadLibrary("Dll3.dll");

typedef int (/*_stdcall*/ *ADDPROC)(inta,int b);

//ADDPROCAdd=(ADDPROC)GetProcAddress(hInst,"?add@@YAHHH@Z");

ADDPROCAdd=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));

if(!Add){

MessageBox("获取函数地址失败!");

return;

}

CString str;

str.Format("5+3=%d",Add(5,3));

FreeLibrary(hInst);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值