• WINDOWS动态链接库--MFC规则动态链接库


      第一代window程序员使用windows api进行编程,到了后来,微软推出MFC类库,于是,动态链接库进行了升级,可以在动态连接库中使用MFC的API,这就叫做MFC动态链接库,

    其中MFC动态链接库又分为两种,MFC规则动态链接库和MFC扩展动态链接库,两者有些不同,一般来说规则动态链接库封装一些函数,方法和自己对MFC方法的封装,而扩展动态链接库

    主要用于扩展MFC的控件,比如MFC的CLIST功能单一,就可以扩展成功能强大的表格,甚至可以扩展到像excel的功能.

      今天说说MFC规则动态链接库,在VS中选择新建一个MFC DLL项目,就可以选择建立规则DLL还是扩展DLL,规则DLL没有WIN32动态链接库所有的DllMain函数,但是它包含有一个从

    CWinApp继承下来的类,theapp在DLL初始化的时候自动调用DllMain,也就是说,类似于MFC编程WinMain函数被封装起来一样,MFC规则动态链接库封装了DllMain.

      在调用规则动态链接库的时候,需要注意两个方面,第一是:静态链接的时候,不需要DLL进行模块状态的切换,第二是动态链接的时候,一定不能忘了切换模块.windows程序都有着自己的资源

    ID,程序启动的时候,默认系统使用的是主线程的资源ID,程序调用的工具栏,菜单,等等资源,都与这个资源ID相关联,或者叫做资源模板.但是当DLL和调用程序都有着自己的资源模板的时候,主线程

    调用DLL显示或者使用某个资源的时候,如果没有切换资源ID,DLL会使用主线程的资源,而不是使用DLL本身的资源,这样就会造成程序的混乱,所以,记住一点,在MFC规则动态链接库中,任何函数的第一行加上

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

      该函数类似于单片机的中断压栈指令,他会将原先的资源模板替换成现在正在使用的实例本身的资源模板,当程序退出这个实例的时候,又会因为析构函数的作用自动恢复原先的资源模板,极为方便.

      MFC规则DLL并非MFC应用程序,他所包含的CWinApp类并不包含消息循环,因为规则DLL不包括MFC的CWinApp::Run()机制,主消息泵依然由应用程序拥有,如果DLL生成非模态对话框,或者自己

    的主窗口框架,则应用程序的主消息泵必须调用从DLL导出来的函数来调用CWinApp的PreTranslateMessage()函数,将消息传送到DLL中

      虽然没有DLLMain函数,但是MFC规则DLL包含另一个用于初始化DLL环境的函数InitInstance(),该函数在DLL项目的主APP入口类文件中,原型如下

     1 // 唯一的一个 CDinkMfcRegularDllApp 对象
     2 
     3 CDinkMfcRegularDllApp theApp;
     4 
     5 
     6 // CDinkMfcRegularDllApp 初始化
     7 
     8 BOOL CDinkMfcRegularDllApp::InitInstance()
     9 {
    10     CWinApp::InitInstance();
    11 
    12     //初始化函数放在这里
    13     dllMessage.Empty();
    14     dllMessage.Append(TEXT("hello mfc regular dll"));
    15 
    16     return TRUE;
    17 }

      和win32 dll一样,规则DLL也可以导出所需的类和函数以及变量,不过有了更方便的方法,MFC定义了三个宏,帮助我们快速导出,分别是

      AFX_EXT_API AFX_EXT_DATA AFX_EXT_CLASS 分别用于导出函数,变量和类,实际上对应的是这三个条件宏,如下

    #ifndef AFX_EXT_DATA
        #ifdef _AFXEXT
            #define AFX_EXT_CLASS       AFX_CLASS_EXPORT
            #define AFX_EXT_API         AFX_API_EXPORT
            #define AFX_EXT_DATA        AFX_DATA_EXPORT
            #define AFX_EXT_DATADEF
        #else
            #define AFX_EXT_CLASS       AFX_CLASS_IMPORT
            #define AFX_EXT_API         AFX_API_IMPORT
            #define AFX_EXT_DATA        AFX_DATA_IMPORT
            #define AFX_EXT_DATADEF
        #endif
    #endif
    #ifndef AFX_DATA_EXPORT
        #define AFX_DATA_EXPORT __declspec(dllexport)
    #endif
    #ifndef AFX_DATA_IMPORT
        #define AFX_DATA_IMPORT __declspec(dllimport)
    #endif
    #ifndef AFX_CLASS_EXPORT
        #define AFX_CLASS_EXPORT __declspec(dllexport)
    #endif
    #ifndef AFX_CLASS_IMPORT
        #define AFX_CLASS_IMPORT __declspec(dllimport)
    #endif
    
    // for global APIs
    #ifndef AFX_API_EXPORT
        #define AFX_API_EXPORT __declspec(dllexport)
    #endif
    #ifndef AFX_API_IMPORT
        #define AFX_API_IMPORT __declspec(dllimport)
    #endif

      我们只需要在项目->属性->VC++->预处理中定义 _AFXEXT这个宏就可以了.

     下面是一个实际的代码演示,首先是头文件

    #pragma once
    #include "stdafx.h"
    
    //动态链接库版本
    #define DINK_MFC_REGULAR_DLL_VERSION    0.01
    
    #define DINK_MFC_REGULAR_DLL_NAME    "DinkMfcRegularDll.dll"
    #define DINK_MFC_REGULAR_LIB_NAME    "DinkMfcRegularDll.lib"
    
    //变量
    EXTERN_C CString AFX_EXT_DATA dllMessage;
    //动态导出变量,需要三个东西,一个是导出函数的load时候的字符串
    //第二个转换空指针到数据指针和实际数据的两个宏定义
    #define DINK_REGULAR_DLL_VAR_DLLMESSAGE        "dllMessage"
    #define MAKE_REGULAR_DLL_DLLMESSAGE_PTR(ptr)    ((CString*)ptr)
    #define MAKE_REGULAR_DLL_DLLMESSAGE_VALUE(ptr)    (*((CString*)ptr))
    
    //全局方法
    EXTERN_C void AFX_EXT_API PathAppendName(CString path,CString name,CString& dst);
    typedef void (*DINK_REGULAR_FUNC_PATHAPPENDNAME)(CString path,CString name,CString& dst);
    #define DINK_REGULAR_DLL_NAME_FUNC_PATHAPPENDNAME    "PathAppendName"
    
    EXTERN_C UINT AFX_EXT_API ShowConfirmDialog(CString& showString);
    
    
    
    //存放所有需要导出的MFC规则DLL的函数,变量,类的头文件
    //类不能动态导出,所以相对比较好一点
    //个人文件处理函数
    class AFX_EXT_CLASS CDinkFileLogic
    {
    public:
        //检测指定文件是否存在
        BOOL FileIsExist(CString filePath,CString fileName);
        //读取指定文件的第一行文本
        BOOL FileReadFirstLine(CString filePath,CString fileName,CString& readStr); 
        //删除指定文件
        BOOL FileRemove(CString filePath,CString fileName);
        //创建指定文件 force表示文件要是已经存在,是否删除并覆盖,为真,删除覆盖
        BOOL FileCreate(CString filePath,CString fileName,BOOL force);
        //创建文件,并添加事件后缀,withTime表示是否添加时间,为false,则创建文件仅仅包含日期
        BOOL FileCreateByTime(CString filePath,CString fileName,BOOL force,BOOL withTime);
        //文件写入一行,isCreate 表示当文件不存在是否主动创建
        BOOL FileWriteLine(CString filePath,CString fileName,CString& writeStr,BOOL isCreate);
        //文件移动,moveOrCopy为真,为剪切操作 为假,是拷贝操作,如果目标文件已经存在,返回失败
        BOOL FileMove(CString SourceFilePath,CString SourceFileName,CString dstFilePath,CString dstFileName,BOOL moveOrCopy);
        //文件移动,moveOrCopy为真,为剪切操作 为假,是拷贝操作,如果目标文件已经存在,默认覆盖该文件
        BOOL FileMoveForce(CString SourceFilePath,CString SourceFileName,CString dstFilePath,CString dstFileName,BOOL moveOrCopy);
    protected:
    
    private:
        BOOL FileIsExist(CString fileFullPath);
    };
    
    
    //个人目录处理函数
    class AFX_EXT_CLASS CDinkDirLogic
    {
    public:
        //检测指定目录是否存在
        BOOL DirIsExist(CString dirPath);
        //创建指定目录,如果目录已经存在,返回OK
        BOOL DirCreate(CString dirPath);
        //创建指定目录,目录名带时间,withTime为真,带时分秒 为假,仅仅年月日
        BOOL DirCreateByTime(CString dirPath,BOOL withTime);
        //移除指定目录,只有在目录为空的时候才能执行
        BOOL DirRemoveByEmpty(CString dirPath);
        //移除指定目录,不管目录是否为空
        BOOL DirRemoveNoEmpty(CString dirPath);
        //检测目标文件夹是否还含有子文件,返回子文件个数
        UINT IsDirContainFile(CString dirPath);
        //获取指定文件夹的子文件夹名和子文件夹个数
        UINT DirListOfDir(CString dirPath,CArray<CString>& dirArray);
        //获取指定文件夹的子文件名列表和子文件个数
        UINT FileListOfDir(CString dirPath,CArray<CString>& fileNameArray);
        //移动指定目录,withFile表示是带文件的移动还是仅仅移动目录
        //不会删除源文件夹
        UINT DirMove(CString sourceDirPath,CString dstDirPath,BOOL withFile);
    protected:
    
    private:
    };

    然后是与之对应的方法文件,数据文件和类文件,我习惯把三个文件分开,比较好维护,如下

    #include "stdafx.h"
    #include "DinkMfcRegularDllExtend.h"
    
    
    BOOL CDinkFileLogic::FileIsExist(CString filePath,CString fileName)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        //检测文件是否存在
        CStdioFile test;
        CString fileFullPath;
        PathAppendName(filePath,fileName,fileFullPath);
        if(test.Open(fileFullPath,CFile::modeRead))
        {
            //可以打开
            test.Close();
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
    
    BOOL CDinkFileLogic::FileIsExist(CString fileFullPath)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        CStdioFile test;
        if(test.Open(fileFullPath,CFile::modeRead))
        {
            //可以打开
            test.Close();
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
    
    BOOL CDinkFileLogic::FileReadFirstLine(CString filePath,CString fileName,CString& readStr)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        //读取制定文件第一行
        CStdioFile test;
        CString fileFullPath;
        PathAppendName(filePath,fileName,fileFullPath);
        if(test.Open(fileFullPath,CStdioFile::modeRead))
        {
            //打开了
            readStr.Empty();
            test.ReadString(readStr);
            test.Close();
            return TRUE;
        }
        else
        {
            //文件打不开
            return FALSE;
        }
    }
    
    BOOL CDinkFileLogic::FileRemove(CString filePath,CString fileName)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        CStdioFile test;
        CString fileFullPath;
        PathAppendName(filePath,fileName,fileFullPath);
        CFile::Remove(fileFullPath);
        return TRUE;
    }
    
    BOOL CDinkFileLogic::FileCreate(CString filePath,CString fileName,BOOL force)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        CStdioFile test;
        CString fileFullPath;
        PathAppendName(filePath,fileName,fileFullPath);
        if(force == TRUE)
        {
            //强制创建,如果存在,清除源文件内容
            if(test.Open(fileFullPath,CStdioFile::modeRead|CFile::modeCreate))
            {
                test.Close();
                //已经存在文件
                CFile::Remove(fileFullPath);
                return TRUE;
            }
            else
            {
                //创建失败
                return FALSE;
            }
        }
        else
        {
            if(test.Open(fileFullPath,CFile::modeCreate|CFile::modeRead|CFile::modeNoTruncate))
            {
                test.Close();
                return TRUE;
            }
            else
            {
                return FALSE;
            }
        }
    }
    
    BOOL CDinkFileLogic::FileCreateByTime(CString filePath,CString fileName,BOOL force,BOOL withTime)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        CString timeStr;
        CTime time;
        time = time.GetCurrentTime();
        if(withTime == TRUE)
        {
            timeStr.Append(time.Format(TEXT("%Y-%m-%d-%H-%M-%S-")));
        }
        else
        {
            timeStr.Append(time.Format(TEXT("%Y-%m-%d-")));
        }
        CString fileFullName(TEXT(""));
        fileFullName.Append(filePath);
        fileFullName.Append(TEXT("\"));
        fileFullName.Append(timeStr);
        fileFullName.Append(fileName);
        CFile fileCreate;
        if(force == TRUE)
        {
            if(fileCreate.Open(fileFullName,CFile::modeRead|CFile::modeCreate))
            {
                fileCreate.Close();
                return TRUE;
            }
            else
            {
                return FALSE;
            }
        }
        else
        {
            if(fileCreate.Open(fileFullName,CFile::modeRead|CFile::modeNoTruncate|CFile::modeCreate))
            {
                fileCreate.Close();
                return TRUE;
            }
            else
            {
                return FALSE;
            }
        }
    }
    
    BOOL CDinkFileLogic::FileWriteLine(CString filePath,CString fileName,CString& writeStr,BOOL isCreate)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        CString fullName;
        PathAppendName(filePath,fileName,fullName);
        CStdioFile dinkFile;
        if(TRUE == isCreate)
        {
            //创建文件,如果文件已经存在就清空
            if(dinkFile.Open(fullName,CFile::modeCreate|CFile::modeReadWrite))
            {
                dinkFile.SeekToEnd();
                dinkFile.WriteString(writeStr);
                dinkFile.Flush();
                dinkFile.Close();
                return TRUE;
            }
            else
            {
                //打开文件失败
                return FALSE;
            }
        }
        else
        {
            //如果文件已经存在就打开而且不清空,不存在就创建
            if(TRUE == dinkFile.Open(fullName,CStdioFile::modeReadWrite|CFile::modeCreate|CFile::modeNoTruncate))
            {
                dinkFile.SeekToEnd();
                dinkFile.WriteString(writeStr+TEXT("
    "));
                dinkFile.Flush();
                dinkFile.Close();
                return TRUE;
            }
            else
            {
                //打开文件失败
                return FALSE;
            }
        }
    }
    
    BOOL CDinkFileLogic::FileMove(CString SourceFilePath,CString SourceFileName,CString dstFilePath,CString dstFileName,BOOL moveOrCopy)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        //文件移动
        return FALSE;
    }
    
    BOOL CDinkFileLogic::FileMoveForce(CString SourceFilePath,CString SourceFileName,CString dstFilePath,CString dstFileName,BOOL moveOrCopy)
    {
        AFX_MANAGE_STATE(AfxGetModuleState());
        return FALSE;
    }
    #include "stdafx.h"
    #include "DinkMfcRegularDllExtend.h"
    #include "DinkConfirmResultDialog.h"
    
    void PathAppendName(CString path,CString name,CString& dst)
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState());
        dst.Empty();
        if (path.IsEmpty())
        {
            dst.Append(TEXT(".\"));
            dst.Append(name);
        }
        else
        {
            dst.Append(path);
            dst.Append(TEXT("\"));
            dst.Append(name);
        }
    }
    
    EXTERN_C UINT AFX_EXT_API ShowConfirmDialog(CString& showString)
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState());
        CDinkConfirmResultDialog dialog(showString);
        int result;
        result = dialog.DoModal();
        return result;
    }
    #include "stdafx.h"
    #include "DinkMfcRegularDllExtend.h"
    
    CString dllMessage;

      对于资源模板,可以这样理解,应用程序及其调用的DLL每一个都有一个系统为之分配的HINSTANCE句柄,进程本身的模块句柄为0X400000(虚拟地址),而DLL的缺省句柄为0X00000000,如果程序加载多个DLL,则每个DLL都有一个不同的HINSSTANCE,

    应用程序加载DLL的时候会对DLL进行重新定位.

      HINSTANCE句柄对于加载资源十分重要,如果DLL需要加载资源就需要将资源句柄指定为DLL的资源句柄,应用程序也可以设置自己资源句柄为DLL的资源句柄,这样可以实现加载DLL的资源,具体在MFC中,切换资源句柄的方法有以下几种

      1.DLL总调用AFX_MANAGE_STATE(AfxGetStaticModuleState()),主要用于DLL函数将应用程序的资源句柄切换为自身的资源句柄

      2.在DLL接口函数调用

    HINSTANCE exeHinstance = AfxGetResourceHandle();
    AfxSetResourceHandle(theApp.m_instance);
    //....接口代码
    AfxSetResourceHandle(exeHinstance);

      实现的效果和第一种类似

      3.应用程序自身切换,这种方法可以实现exe自身加载不同的资源

    HINSTANCE exe_hinstance = GetModuleHandle(NULL);
    HINSTANCE dll_hinstance = GetModuleHandle("DLL名称");
    AfxSetResourceHandle(dll_hinstance);
    //调用dll函数,或者调用dll资源
    AfxSetResourceHandle(exe_hinstance);

    以下为静态调用MFC规则DLL的演示程序,如下

    //静态调用变量
    #include "..\DinkMfcRegularDllDinkMfcRegularDllExtend.h"
    #pragma comment(lib,DINK_MFC_REGULAR_LIB_NAME)
    
    void CDinkMfcRegularDllCallDlg::OnBnClickedButtonStaticShowVar()
    {
        // TODO: 在此添加控件通知处理程序代码
        CString showString(TEXT("load var str is : "));
        showString.Append(dllMessage);
        MessageBox(showString,TEXT("message"),MB_OK);
    }
    
    //静态
    void CDinkMfcRegularDllCallDlg::OnBnClickedButtonStaticFuncCall()
    {
        // TODO: 在此添加控件通知处理程序代码
        CString str1(TEXT("F:\MFC\DinkDll\DinkDll\Release"));
        CString str2(TEXT("DinkMfcRegularDll.dll"));
        CString dstStr;
        PathAppendName(str1,str2,dstStr);
        MessageBox(dstStr,TEXT("message"),MB_OK);
    }
    
    //静态调用类
    void CDinkMfcRegularDllCallDlg::OnBnClickedButtonStaticClassCall()
    {
        // TODO: 在此添加控件通知处理程序代码
        CDinkFileLogic dinkFileLogic;
        CString str1(TEXT("F:\MFC\DinkDll\DinkDll\Release"));
        CString str2(TEXT("Hello.txt"));
        CString dstStr;
        if(dinkFileLogic.FileCreate(str1,str2,FALSE))
        {
            if(dinkFileLogic.FileWriteLine(str1,str2,CString("hello world"),FALSE))
            {
                MessageBox(TEXT("write file ok"),TEXT("message"),MB_ICONINFORMATION|MB_OK);
            }
            else
            {
                MessageBox(TEXT("write file failed"),TEXT("error"),MB_ICONERROR|MB_OK);
            }
        }
        else
        {
            MessageBox(TEXT("file create failed"),TEXT("error"),MB_ICONERROR|MB_OK);
        }
    }
    
    
    void CDinkMfcRegularDllCallDlg::OnBnClickedButtonShowDialog()
    {
        // TODO: 在此添加控件通知处理程序代码
        UINT result;
        result = ShowConfirmDialog(CString("hello world"));
        CString confirmShowMessage(TEXT(""));
        confirmShowMessage.AppendFormat(TEXT("dialog return value is %d"),result);
        MessageBox(confirmShowMessage,TEXT("message"),MB_ICONINFORMATION|MB_OK);
    }

    以下为动态调用DLL

    //动态变量显式
    void CDinkMfcRegularDllCallDlg::OnBnClickedButtonDynamicShowVar()
    {
        // TODO: 在此添加控件通知处理程序代码
        HMODULE dllModule;
        CString* str;
        dllModule = AfxLoadLibrary(TEXT(DINK_MFC_REGULAR_DLL_NAME));
        if(dllModule != NULL)
        {
            str = MAKE_REGULAR_DLL_DLLMESSAGE_PTR(GetProcAddress(dllModule,DINK_REGULAR_DLL_VAR_DLLMESSAGE));
            CString showString(TEXT("load var str is : "));
            showString.Append(*str);
            MessageBox(showString,TEXT("message"),MB_OK);
            AfxFreeLibrary(dllModule);
        }
        else
        {
            MessageBox(TEXT("lib load failed"),TEXT("error"),MB_ICONERROR);
        }
    }
    
    //动态调用函数
    void CDinkMfcRegularDllCallDlg::OnBnClickedButtonDynamicCallFunc()
    {
        // TODO: 在此添加控件通知处理程序代码
        HMODULE dllModule;
        DINK_REGULAR_FUNC_PATHAPPENDNAME pathAddFunc;
        dllModule = AfxLoadLibrary(TEXT(DINK_MFC_REGULAR_DLL_NAME));
        if(dllModule != NULL)
        {
            pathAddFunc = (DINK_REGULAR_FUNC_PATHAPPENDNAME)(GetProcAddress(dllModule,DINK_REGULAR_DLL_NAME_FUNC_PATHAPPENDNAME));
            CString str1(TEXT("F:\MFC\DinkDll\DinkDll\Release"));
            CString str2(TEXT("DinkMfcRegularDll.dll"));
            CString dstStr;
            pathAddFunc(str1,str2,dstStr);
            MessageBox(dstStr,TEXT("message"),MB_OK);
            AfxFreeLibrary(dllModule);
        }
        else
        {
            MessageBox(TEXT("lib load failed"),TEXT("error"),MB_ICONERROR);
        }
    }

    MFC规则DLL,1.动态的切换应用程序的资源.2.能够在DLL中使用MFC的API

    但是MFC dll也不能动态的调用dll中的类,要使用类,使用静态调用.

      

  • 相关阅读:
    视频直播架构
    day1 python 入门
    python 多用户登录
    mysql innobackup 备份脚本
    ADT离线安装
    真机调试adb:wait for device 解决方案
    php中的魔术方法
    整理资料
    PostgreSQL表空间_数据库_模式_表_用户角色之间的关系[转]
    PHP获取文件夹的大小
  • 原文地址:https://www.cnblogs.com/dengxiaojun/p/5342620.html
Copyright © 2020-2023  润新知