VC++动态链接库编程之MFC扩展 DLL.docx
《VC++动态链接库编程之MFC扩展 DLL.docx》由会员分享,可在线阅读,更多相关《VC++动态链接库编程之MFC扩展 DLL.docx(11页珍藏版)》请在冰豆网上搜索。
VC++动态链接库编程之MFC扩展DLL
VC++动态链接库编程之MFC扩展DLL
资料引用:
DLL类型入口函数非MFCDLL编程者提供DllMain函数MFC规则DLLCWinApp对象的InitInstance和ExitInstanceMFC扩展DLLMFCDLL向导生成DllMain函数
对于MFC扩展DLL,系统会自动在工程中添加如下表所示的宏,这些宏为DLL和应用程序的编写提供了方便。
像AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA这样的宏,在DLL和应用程序中将具有不同的定义,这取决于_AFXEXT宏是否被定义。
这使得在DLL和应用程序中,使用统一的一个宏就可以表示出输出和输入的不同意思。
在DLL中,表示输出(因为_AFXEXT被定义,通常是在编译器的标识参数中指定/D_AFXEXT);在应用程序中,则表示输入(_AFXEXT没有定义)。
宏定义AFX_CLASS_IMPORT__declspec(dlleXPort)AFX_API_IMPORT__declspec(dllexport)AFX_DATA_IMPORT__declspec(dllexport)AFX_CLASS_EXPORT__declspec(dllexport)AFX_API_EXPORT__declspec(dllexport)AFX_DATA_EXPORT__declspec(dllexport)AFX_EXT_CLASS#ifdef_AFXEXT
AFX_CLASS_EXPORT
#else
AFX_CLASS_IMPORTAFX_EXT_API#ifdef_AFXEXT
AFX_API_EXPORT
#else
AFX_API_IMPORTAFX_EXT_DATA#ifdef_AFXEXT
AFX_DATA_EXPORT
#else
AFX_DATA_IMPORT
6.2MFC扩展DLL导出MFC派生类
在这个例子中,我们将产生一个名为“ExtDll”的MFC扩展DLL工程,在这个DLL中导出一个对话框类,这个对话框类派生自MFC类CDialog。
使用MFC向导生成MFC扩展DLL时,系统会自动添加如下代码:
staticAFX_EXTENSION_MODULEExtDllDLL={NULL,NULL};
extern"C"intAPIENTRY
DllMain(HINSTANCEhInstance,DWorddwReason,LPVOIDlPReserved)
{
//RemovethisifyouuselpReserved
UNREFERENCED_PARAMETER(lpReserved);
//说明:
lpReserved是一个被系统所保留的参数,对于隐式链接是一个非零值,对于显式链接值是零
if(dwReason==DLL_PROCESS_ATTACH)
{
TRACE0("EXTDLL.DLLInitializing!
\n");
//ExtensionDLLone-timeinitialization
if(!
AfxInitExtensionModule(ExtDllDLL,hInstance))
return0;
//InsertthisDLLintotheresourcechain
newCDynLinkLibrary(ExtDllDLL);
}
elseif(dwReason==DLL_PROCESS_DETACH)
{
TRACE0("EXTDLL.DLLTerminating!
\n");
//TerminatethelibrarybeforedestrUCtorsarecalled
AfxTermExtensionModule(ExtDllDLL);
}
return1;//ok
}
这一段代码含义晦涩,我们需要对其进行解读:
(1)上述代码完成MFC扩展DLL的初始化和终止处理;
(2)初始化期间所创建的CDynLinkLibrary对象使MFC扩展DLL可以将DLL中的CRuntimeClass对象或资源导出到应用程序;
(3)AfxInitExtensionModule函数捕捉模块的CRuntimeClass结构和在创建CDynLinkLibrary对象时使用的对象工厂(COleObjectFactory对象);
(4)AfxTermExtensionModule函数使MFC得以在每个进程与扩展DLL分离时(进程退出或使用AfxFreeLibrary卸载DLL时)清除扩展DLL;
(5)第一条语句staticAFX_EXTENSION_MODULEExtDllDLL={NULL,NULL};定义了一个AFX_EXTENSION_MODULE类的静态全局对象,AFX_EXTENSION_MODULE的定义如下:
structAFX_EXTENSION_MODULE
{
BOOLbInitialized;
HMODULEhModule;
HMODULEhResource;
CRuntimeClass*pFirstSharedClass;
COleObjectFactory*pFirstSharedFactory;
};
由AFX_EXTENSION_MODULE的定义我们可以更好的理解
(2)、(3)、(4)点。
在资源编辑器中添加一个如图15所示的对话框,并使用MFC类向导为其添加一个对应的类CExtDialog,系统自动添加了ExtDialog.h和ExtDialog.cpp两个头文件。
图15MFC扩展DLL中的对话框
修改ExtDialog.h中CExtDialog类的声明为:
classAFX_EXT_CLASSCExtDialog:
publicCDialog
{
public:
CExtDialog(CWnd*pParent=NULL);
enum{IDD=IDD_DLL_DIALOG};
protected:
virtualvoidDoDataExchange(CDataExchange*pDX);
DECLARE_MESSAGE_MAP()
};
这其中最主要的改变是我们在classAFX_EXT_CLASSCExtDialog语句中添加了“AFX_EXT_CLASS”宏,则使得DLL中的CExtDialog类被导出。
6.3MFC扩展DLL的加载
6.3.1隐式加载
我们在6.2工程所在的工作区中添加一个LoadExtDllDlg工程,用于演示MFC扩展DLL的加载。
在LoadExtDllDlg工程中添加一个如图16所示的对话框,这个对话框上包括一个“调用DLL”按钮。
图16MFC扩展DLL调用工程中的对话框
在与图16对应对话框类实现文件的头部添加:
//LoadExtDllDlg.cpp:
implementationfile
//
#include"..\ExtDialog.h"
#pragmacomment(lib,"ExtDll.lib")
而“调用DLL”按钮的单击事件的消息处理函数为:
voidCLoadExtDllDlg:
:
OnDllcallButton()
{
CExtDialogextDialog;
extDialog.DoModal();
}
当我们单击“调用DLL”的时候,弹出了如图15的对话框。
为提供给用户隐式加载(MFC扩展DLL一般使用隐式加载,具体原因见下节),MFC扩展DLL需要提供三个文件:
(1)描述DLL中扩展类的头文件;
(2)与动态链接库对应的.LIB文件;
(3)动态链接库.DLL文件本身。
有了这三个文件,应用程序的开发者才可充分利用MFC扩展DLL。
6.3.2显示加载
显示加载MFC扩展DLL应使用MFC全局函数AfxLoadLibrary而不是WIN32API中的LoadLibrary。
AfxLoadLibrary最终也调用了LoadLibrary这个API,但是在调用之前进行了线程同步的处理。
AfxLoadLibrary的函数原型与LoadLibrary完全相同,为:
HINSTANCEAFXAPIAfxLoadLibrary(LPCTSTRlpszModuleName);
与之相对应的是,MFC应用程序应使用AfxFreeLibrary而非FreeLibrary卸载MFC扩展DLL。
AfxFreeLibrary的函数原型也与FreeLibrary完全相同,为:
BOOLAFXAPIAfxFreeLibrary(HINSTANCEhInstLib);
假如我们把上例中的“调用DLL”按钮单击事件的消息处理函数改为:
voidCLoadExtDllDlg:
:
OnDllcallButton()
{
HINSTANCEhDll=AfxLoadLibrary("ExtDll.dll");
if(NULL==hDll)
{
AfxMessageBox("MFC扩展DLL动态加载失败");
return;
}
CExtDialogextDialog;
extDialog.DoModal();
AfxFreeLibrary(hDll);
}
则工程会出现link错误:
LoadExtDllDlg.obj:
errorLNK2001:
unresolvedexternalsymbol"__declspec(dllimport)public:
virtual__thiscallCExtDialog:
:
~CExtDialog(void)"(__imp_?
?
1CExtDialogUAE@XZ)
LoadExtDllDlg.obj:
errorLNK2001:
unresolvedexternalsymbol"__declspec(dllimport)public:
__thiscallCExtDialog:
:
CExtDialog(classCWnd*)"(__imp_?
?
0CExtDialogQAE@PAVCWnd@Z)
提示CExtDialog的构造函数和析构函数均无法找到!
是的,对于派生MFC类的MFC扩展DLL,当我们要在应用程序中使用DLL中定义的派生类时,我们不宜使用动态加载DLL的方法。
6.4MFC扩展DLL加载MFC扩展DLL
我们可以在MFC扩展DLL中再次使用MFC扩展DLL,但是,由于在两个DLL中对于AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA宏的定义都是输出,这会导致调用的时候出现问题。
我们将会在调用MFC扩展DLL的DLL中看到link错误:
errorLNK2001:
unresolvedexternalsymbol….......
因此,在调用MFC扩展DLL的MFC扩展DLL中,在包含被调用DLL的头文件之前,需要临时重新定义AFX_EXT_CLASS的值。
下面的例子显示了如何实现:
//临时改变宏的含义“输出”为“输入”
#undefAFX_EXT_CLASS
#undefAFX_EXT_API
#undefAFX_EXT_DATA
#defineAFX_EXT_CLASSAFX_CLASS_IMPORT
#defineAFX_EXT_APIAFX_API_IMPORT
#defineAFX_EXT_DATAAFX_DATA_IMPORT
//包含被调用MFC扩展DLL的头文件
#include"CalledDLL.h"
//恢复宏的含义为输出
#undefAFX_EXT_CLASS
#undefAFX_EXT_API
#undefAFX_EXT_DATA
#defineAFX_EXT_CLASSAFX_CLASS_EXPORT
#defineAFX_EXT_APIAFX_API_EXPORT
#defineAFX_EXT_DATAAFX_DATA_EXPORT
6.5MFC扩展DLL导出函数和变量
MFC扩展DLL导出函数和变量的方法也十分简单,下面我们给出一个简单的例子。
我们在MFC向导生成的MFC扩展DLL工程中添加gobal.h和global.cpp两个文件:
//global.h:
MFC扩展DLL导出变量和函数的声明
extern"C"
{
intAFX_EXT_DATAtotal;//导出变量
intAFX_EXT_APIadd(intx,inty);//导出函数
}
//global.cpp:
MFC扩展DLL导出变量和函数定义
#include"StdAfx.h"
#include"global.h"
extern"C"inttotal;
intadd(intx,inty)
{
total=x+y;
returntotal;
}
编写一个简单的控制台程序来调用这个MFC扩展DLL:
#include
#include单击此处下载本工程)。
我们知道static控件所对应的CStatic类不具备设置背景和文本颜色的接口,这使得我们不能在对话框或其它用户界面上自由灵活地修改static控件的颜色风格,因此我们需要一个提供了SetBackColor和SetTextColor接口的CStatic派生类CMultiColorStatic。
这个类的声明如下:
classAFX_EXT_CLASSCMultiColorStatic:
publicCStatic
{
//Construction
public:
CMultiColorStatic();
virtual~CMultiColorStatic();
//Attributes
protected:
CStringm_strCaption;
COLORREFm_BackColor;
COLORREFm_TextColor;
//Operations
public:
voidSetTextColor(COLORREFTextColor);
voidSetBackColor(COLORREFBackColor);
voidSetCaption(CStringstrCaption);
//Generatedmessagemapfunctions
protected:
afx_msgvoidOnPaint();
DECLARE_MESSAGE_MAP()
};
在这个类的实现文件中,我们需要为它提供WM_PAINT消息的处理函数(这是因为颜色的设置依靠于WM_PAINT消息):
BEGIN_MESSAGE_MAP(CMultiColorStatic,CStatic)
//{{AFX_MSG_MAP(CMultiColorStatic)
ON_WM_PAINT()//为这个类定义WM_PAINT消息处理函数
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
下面是这个类中的重要成员函数:
//为CMultiColorStatic类添加“设置文本颜色”接口
voidCMultiColorStatic:
:
SetTextColor(COLORREFTextColor)
{
m_TextColor=TextColor;//设置文字颜色
}
//为CMultiColorStatic类添加“设置背景颜色”接口
voidCMultiColorStatic:
:
SetBackColor(COLORREFBackColor)
{
m_BackColor=BackColor;//设置背景颜色
}
//为CMultiColorStatic类添加“设置标题”接口
voidCMultiColorStatic:
:
SetCaption(CStringstrCaption)
{
m_strCaption=strCaption;
}
//重画Static,颜色和标题的设置都依靠于这个函数
voidCMultiColorStatic:
:
OnPaint()
{
CPaintDCdc(this);//devicecontextforpainting
CRectrect;
GetClientRect(&rect);
dc.SetBkColor(m_BackColor);
dc.SetBkMode(TRANSPARENT);
CFont*pFont=GetParent()->GetFont();//得到父窗体的字体
CFont*pOldFont;
pOldFont=dc.SelectObject(pFont);//选用父窗体的字体
dc.SetTextColor(m_TextColor);//设置文本颜色
dc.DrawText(m_strCaption,&rect,DT_CENTER);//文本在Static中心
dc.SelectObject(pOldFont);
}
为了验证CMultiColorStatic类,我们制作一个基于对话框的应用程序,它包含一个如图17所示的对话框。
该对话框上包括一个static控件和三个按钮,这三个按钮可分别把static控件设置为“红色”、“蓝色”和“绿色”。
图17扩展的CStatic类调用演示
下面看看应如何编写与这个对话框对应的类。
包含这种Static的对话框类的声明如下:
#include"..\MultiColorStatic.h"
#pragmacomment(lib,"ColorStatic.lib")
//CCallDllDlgdialog
classCCallDllDlg:
publicCDialog
{
public:
CCallDllDlg(CWnd*pParent=NULL);//standardconstructor
enum{IDD=IDD_CALLDLL_DIALOG};
CMultiColorStaticm_colorstatic;//包含一个CMultiColorStatic的实例
protected:
virtualvoidDoDataExchange(CDataExchange*pDX);//DDX/DDVsupport
HICONm_hIcon;
//Generatedmessagemapfunctions
//{{AFX_MSG(CCallDllDlg)
virtualBOOLOnInitDialog();
afx_msgvoidOnSysCommand(UINTnID,LPARAMlParam);
afx_msgvoidOnPaint();
afx_msgHCURSOROnQueryDragIcon();
afx_msgvoidOnRedButton();
afx_msgvoidOnBlueButton();
afx_msgvoidOnGreenButton();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
下面是这个类中与使用CMultiColorStatic相关的主要成员函数:
voidCCallDllDlg:
:
DoDataExchange(CDataExchange*pDX)
{
CDialog:
:
DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCallDllDlg)
DDX_Control(pDX,IDC_COLOR_STATIC,m_colorstatic);
//使m_colorstatic与IDC_COLOR_STATIC控件关联
//}}AFX_DATA_MAP
}
BOOLCCallDllDlg:
:
OnInitDialog()
{
…
//TODO:
Addextrainitializationhere
//初始static控件的显示
m_colorstatic.SetCaption("最开始为黑色");
m_colorstatic.SetTextColor(RGB(0,0,0));
returnTRUE;//returnTRUEunlessyousetthefocustoacontrol
}
//设置static控件文本颜色为红色
voidCCallDllDlg:
:
OnRedButton()
{
m_colorstatic.SetCaption("改变为红色");
m_colorstatic.SetTextColor(RGB(255,0,0));
Invalidate(TRUE);//导致发出WM_PAINT消息
}
//设置static控件文本颜色为蓝色
voidCCallDllDlg:
:
OnBlueButton()
{
m_colorstatic.SetCaption("改变为蓝色");
m_colorstatic.SetTextColor(RGB(0,0,255));
Invalidate(TRUE);//导致发出WM_PAINT消息
}
//设置static控件文本颜色为绿色
voidCCallDllDlg:
:
OnGreenButton()
{
m_colorstatic.SetCaption("改变为绿色");
m_colorstatic.SetTextColor(RGB(0,255,0));
Invalidate(TRUE);//导致发出WM_PAINT消息
}
至此,我们已经讲解完成了所有类型的动态链接库,即非MFCDLL、MFC规则DLL和MFC扩展DLL。
下一节将给出DLL的三个工程实例,与读者朋友们共同体会DLL的应用范围和使用方法。
资料引用: