欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

DLL详解(下)---MFC下的DLL

程序员文章站 2022-03-04 19:07:46
...


(本文针对的是使用MFC下的DLL)

MFC规则DLL

何为规则DLL(Regular DLL)

(1)该DLL是基于MFC的,可以在这种DLL内部使用MFC。
(2)该DLL是"规则"的,它不同于"MFC扩展DLL",在规则DLL中内部虽然是可以使用MFC,但是规则DLL的接口应该不能是基于MFC的。而MFC扩展DLL与应用程序接口可以是MFC,可以从MFC扩展dll中导出一个MFC的派生类。

Regular DLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。在这种动态连接库中,包含一个从CWinApp继承下来的类,DllMain函数则由MFC自动提供。

共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为 DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。(解释:EXE模块在进程虚拟空间中的起始地址 。 进程本身的模块句柄一般为0x400000 , 而DLL模块的缺省句柄为0x10000000 。 如果程序同时加载了多个DLL,则每个DLL模块都会有不同的 HINSTANCE 。 应用程序在加载DLL时对其进行了重定位。)

模块的切换有三种方式:

  1. 在DLL函数中调用AFX_MANAGE_STATE(AfxGetStaticModuleState());

    void ShowDlg(void)
    {
    	//在函数开始处变更,在函数结束时恢复
    	//将AFX_MANAGE_STATE(AfxGetStaticModuleState());
    	//作为接口函数的第一条语句进行模块状态切换
    	AFX_MANAGE_STATE(AfxGetStaticModuleState());
    	CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框
    	dlg.DoModal();
    
    }
    
  2. 在DLL函数中调用AfxGetResourceHandle();

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

    我们将DLL中的接口函数ShowDlg改为:

    void ShowDlg(void)   
    {   
        //方法2的状态变更   
         HINSTANCE save_hInstance = AfxGetResourceHandle();   
         AfxSetResourceHandle(theApp.m_hInstance);   
         CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框   
         dlg.DoModal();   
         //方法2的状态还原   
         AfxSetResourceHandle(save_hInstance);   
    }  
    

    ​ 通过AfxGetResourceHandleAfxSetResourceHandle的合理变更,我们能够灵活地设置程序的资源模块句柄,而方法一则只能在DLL接口函数退出的时候才会恢复模块句柄。方法二则不同,如果将ShowDlg改为:

    extern CSharedDllApp theApp; //需要声明theApp外部全局变量   
    void ShowDlg(void)   
    {   
        //方法2的状态变更   
        HINSTANCE save_hInstance = AfxGetResourceHandle();   
        AfxSetResourceHandle(theApp.m_hInstance);   
        CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框   
        dlg.DoModal();   
        //方法2的状态还原   
        AfxSetResourceHandle(save_hInstance);   
        //使用方法2后在此处再进行操作针对的将是应用程序的资源   
        CDialog dlg1(IDD_DLL_DIALOG); //打开ID为2000的对话框   
        dlg1.DoModal();   
    }   
    

    在应用程序主对话框的“调用DLL”按钮上点击,将看到两个对话框,相继为DLL中的对话框(图6)和EXE中的对话框(图7)。

  3. 由应用程序自身切换(不推荐,最麻烦)

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

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

    void ShowDlg(void)   
    {   
        CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框   
        dlg.DoModal();   
    }   
    

    而将应用程序的OnCalldllButton函数改为:

    void CSharedDllCallDlg::OnCalldllButton()   
    {   
        //方法3:由应用程序本身进行状态切换   
        //获取EXE模块句柄   
        HINSTANCE exe_hInstance = GetModuleHandle(NULL);   
        //或者HINSTANCE exe_hInstance = AfxGetResourceHandle();   
        //获取DLL模块句柄   
        HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll");   
        AfxSetResourceHandle(dll_hInstance); //切换状态   
        ShowDlg(); //此时显示的是DLL的对话框   
        AfxSetResourceHandle(exe_hInstance); //恢复状态   
        //资源模块恢复后再调用ShowDlg   
        ShowDlg(); //此时显示的是EXE的对话框   
    }  
    

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

    ​ 方法三与方法二的不同在于,方法三是在应用程序中利用AfxGetResourceHandleAfxSetResourceHandle进行资源模块句柄切换的。同样地,在应用程序主对话框的“调用DLL”按钮上点击,也将看到两个对话框,相继为DLL中的对话框和EXE中的对话框。

MFC规则DLL的创建

​ MFC规则DLL并不是MFC应用程序,它所继承自CWinApp的类不包含消息循环。这是因为,MFC规则DLL不包含CWinApp::Run机制,主消息泵仍然由应用程序拥有。如果DLL 生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从DLL 导出的函数来调用PreTranslateMessage成员函数。

(内存泄漏的一种可能原因是MFC创建了在消息处理函数内部使用的临时对象。在MFC应用程序中,这些临时对象将CWinApp::OnIdle()在处理消息之间调用的函数中自动清除。但是,在MFC动态链接库(DLL)中,OnIdle()不会自动调用该函数。结果,临时对象不会自动清除。若要清除临时对象,DLL必须OnIdle(1)定期显式调用。)

在进程附加时,该_DllMainCRTStartup函数设置缓冲区安全性检查,初始化CRT和其他库,初始化运行时类型信息,初始化并调用静态和非本地数据的构造函数,初始化线程本地存储,为该线程增加一个内部静态计数器每个附件,然后调用用户或库提供的DllMain

因为常规MFC DLL具有一个CWinApp对象,所以它们应在与MFC应用程序相同的位置执行初始化和终止任务:在DLL 派生类的InitInstanceExitInstance成员函数中CWinApp。因为MFC提供了for 和DllMain调用的函数,所以您不应编写自己的函数。MFC提供的函数在加载DLL时调用,并且在DLL卸载之前调用。_DllMainCRTStartup``DLL_PROCESS_ATTACH``DLL_PROCESS_DETACH``DllMain``DllMain``InitInstance``ExitInstance

参考链接:https://blog.csdn.net/nupt123456789/article/details/7343820

带静态链接MFC的规则的DLL

静态链接到MFC的规则DLL与MFC库(包括MFC扩展 DLL)静态链接,将MFC库的代码直接生成在.dll文件中。在调用这种DLL的接口时,MFC使用DLL的资源。因此,在静态链接到MFC 的规则DLL中不需要进行模块状态的切换。这种DLL是使用MFC的静态链接库版本构建的。函数通常使用标准C接口从常规MFC DLL中导出。(将DLL的相关代码写进EXE)

功能:

  1. 客户端可执行文件可以用任何支持DLL使用的语言编写,它不必是MFC应用程序。
  2. DLL可以链接到应用程序使用的同一MFC静态链接库。DLL的静态链接库不再有单独的版本。
  3. 在MFC的4.0版之前,USRDLL提供的功能类型与静态链接到MFC的常规MFC DLL相同。从Visual C ++版本4.0开始,术语USRDLL已过时。

要求

  1. 此类DLL必须实例化从派生的类CWinApp
  2. 这种DLL使用DllMainMFC提供的。InitInstanceExitInstance普通MFC应用程序一样,将所有特定于DLL的初始化代码放在成员函数中,并将终止代码放在其中。
  3. 即使术语USRDLL已过时,您仍然必须在编译器命令行上定义“ _USRDLL ”。此定义确定从MFC头文件中提取哪些声明。

常规MFC DLL中的所有内存分配都应保留在DLL中;DLL不应传递给调用可执行文件或从其接收以下任何内容:

  • 指向MFC对象的指针
  • 指向由MFC分配的内存的指针

使用共享MFC DLL的规则DLL

打包时MFC的DLL的内容没有被包含在EXE文件中,运行时要求系统中要有相关的DLL文件。(MFC: Microsoft Foundation Classes 可以理解为是一个工具,它是在C++环境下编写应用程序的一个框架和引擎。)

在动态链接到MFC的常规MFC DLL中所有导出功能的开头添加宏AFX_MANAGE_STATE(但是不能在常规MFC的DLL静态链接到MFC或MFC扩展DLL使用。),以将当前模块状态设置为DLL的状态。这是通过将以下代码行添加到从DLL导出的函数的开头来完成的:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

动态链接到MFC的常规MFC DLL具有以下功能:

  • 这是Visual C ++ 4.0引入的新型DLL。
  • 客户端可执行文件可以用任何支持DLL使用的语言(C,C ++,Pascal,Visual Basic等)编写;它不必是MFC应用程序。
  • 与静态链接的常规MFC DLL不同,这种类型的DLL是动态链接到MFC DLL(也称为共享MFC DLL)的。
  • 链接到此类DLL的MFC导入库与MFC扩展DLL或使用MFC DLL的应用程序所使用的库相同:MFCxx(D).lib。

动态链接到MFC的常规MFC DLL具有以下要求:

  • 这些DLL使用定义的**_AFXDLL进行编译,就像动态链接到MFC DLL的可执行文件一样。但是也定义了_USRDLL**,就像静态链接到MFC的常规MFC DLL一样。
  • 这种类型的DLL必须实例化CWinApp派生的类。
  • 这种DLL使用DllMainMFC提供的。InitInstanceExitInstance普通MFC应用程序一样,将所有特定于DLL的初始化代码放在成员函数中,并将终止代码放在其中。

MFC扩展DLL

MFC扩展DLL的内涵为MFC的扩展,是使用MFC的动态链接库版本(也称为MFC的共享版本)构建的。只有使用MFC共享版本构建的MFC可执行文件(应用程序或常规MFC DLL)才可以使用MFC扩展DLL。

扩展DLL也可以用于在应用程序和DLL之间传递MFC派生的对象。与传递的对象关联的成员函数存在于创建对象的模块中。因为使用共享的DLL版本的MFC时这些函数已正确导出,所以您可以在应用程序与其加载的MFC扩展DLL之间*传递MFC或MFC派生的对象指针。

MFC扩展DLL具有以下功能和要求:

  • 客户端可执行文件必须是使用_AFXDLL定义编译的MFC应用程序。
  • 动态链接到MFC的常规MFC DLL也可以使用MFC扩展DLL。
  • MFC扩展DLL应使用_AFXEXT定义进行编译。这_AFXDLL也要进行定义,并确保从MFC头文件中提取正确的声明。它还确保将AFX_EXT_CLASS其定义为__declspec(dllexport)在构建DLL时的定义,如果使用此宏在MFC扩展DLL中声明类,则这是必需的。
  • MFC扩展DLL不应实例化从派生的类CWinApp,而应依靠客户端应用程序(或DLL)来提供此对象。
  • 但是,MFC扩展DLL应该提供一个DllMain功能并在那里进行任何必要的初始化。

MFC扩展DLL使用MFC的共享版本的方式与应用程序使用MFC的共享DLL版本的方式相同,但还有一些其他注意事项:

  • 没有CWinApp派生的对象。它必须与CWinApp客户端应用程序的-derived对象一起使用。这意味着客户端应用程序拥有主消息泵,空闲循环等。
  • 它调用AfxInitExtensionModuleDllMain函数。应该检查该函数的返回值。如果从AfxInitExtensionModule返回零值,则从DllMain函数返回0 。
  • 如果MFC扩展DLL想要将对象或资源导出到应用程序,则它将在初始化期间创建CDynLinkLibrary对象CRuntimeClass

在MFC的4.0版之前,这种类型的DLL被称为AFXDLL。AFXDLL是指_AFXDLL在构建DLL时定义的预处理器符号。

(本文大部分都是参考了微软官方文档)


本文作者:"( ̄y▽, ̄)╭ "
本文链接:https://blog.csdn.net/weixin_42703404/article/details/106065436
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!


相关标签: c++ c++