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("\n"));
            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 "..\\DinkMfcRegularDll\DinkMfcRegularDllExtend.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中的類,要使用類,使用靜態調用.

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM