第一代window程序員使用windows api進行編程,到了後來,微軟推出MFC類庫,因而,動態連接庫進行了升級,能夠在動態鏈接庫中使用MFC的API,這就叫作MFC動態連接庫,程序員
其中MFC動態連接庫又分爲兩種,MFC規則動態連接庫和MFC擴展動態連接庫,二者有些不一樣,通常來講規則動態連接庫封裝一些函數,方法和本身對MFC方法的封裝,而擴展動態連接庫編程
主要用於擴展MFC的控件,好比MFC的CLIST功能單一,就能夠擴展成功能強大的表格,甚至能夠擴展到像excel的功能.windows
今天說說MFC規則動態連接庫,在VS中選擇新建一個MFC DLL項目,就能夠選擇創建規則DLL仍是擴展DLL,規則DLL沒有WIN32動態連接庫全部的DllMain函數,可是它包含有一個從api
CWinApp繼承下來的類,theapp在DLL初始化的時候自動調用DllMain,也就是說,相似於MFC編程WinMain函數被封裝起來同樣,MFC規則動態連接庫封裝了DllMain.app
在調用規則動態連接庫的時候,須要注意兩個方面,第一是:靜態連接的時候,不須要DLL進行模塊狀態的切換,第二是動態連接的時候,必定不能忘了切換模塊.windows程序都有着本身的資源框架
ID,程序啓動的時候,默認系統使用的是主線程的資源ID,程序調用的工具欄,菜單,等等資源,都與這個資源ID相關聯,或者叫作資源模板.可是當DLL和調用程序都有着本身的資源模板的時候,主線程函數
調用DLL顯示或者使用某個資源的時候,若是沒有切換資源ID,DLL會使用主線程的資源,而不是使用DLL自己的資源,這樣就會形成程序的混亂,因此,記住一點,在MFC規則動態連接庫中,任何函數的第一行加上工具
AFX_MANAGE_STATE(AfxGetStaticModuleState());
該函數相似於單片機的中斷壓棧指令,他會將原先的資源模板替換成如今正在使用的實例自己的資源模板,當程序退出這個實例的時候,又會由於析構函數的做用自動恢復原先的資源模板,極爲方便.spa
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中的類,要使用類,使用靜態調用.