王朝网络
分享
 
 
 

VC++动态链接库编程之MFC扩展 DLL

王朝vc·作者佚名  2008-06-01
宽屏版  字体: |||超大  

DLL类型入口函数 非 MFC DLL 编程者提供DllMain函数 MFC规则 DLL CWinApp对象的InitInstance 和 ExitInstance MFC扩展 DLL MFC DLL向导生成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_IMPORT AFX_EXT_API #ifdef _AFXEXT

AFX_API_EXPORT

#else

AFX_API_IMPORT AFX_EXT_DATA #ifdef _AFXEXT

AFX_DATA_EXPORT

#else

AFX_DATA_IMPORT

6.2 MFC扩展DLL导出MFC派生类

在这个例子中,我们将产生一个名为“ExtDll”的MFC扩展DLL工程,在这个DLL中导出一个对话框类,这个对话框类派生自MFC类CDialog。

使用MFC向导生成MFC扩展DLL时,系统会自动添加如下代码:

static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };

extern "C" int APIENTRY

DllMain( HINSTANCE hInstance, DWord dwReason, LPVOID lPReserved )

{

// Remove this if you use lpReserved

UNREFERENCED_PARAMETER( lpReserved );

//说明:lpReserved是一个被系统所保留的参数,对于隐式链接是一个非零值,对于显式链接值是零

if (dwReason == DLL_PROCESS_ATTACH)

{

TRACE0( "EXTDLL.DLL Initializing!\n" );

// Extension DLL one-time initialization

if ( !AfxInitExtensionModule( ExtDllDLL, hInstance ))

return 0;

// Insert this DLL into the resource chain

new CDynLinkLibrary( ExtDllDLL );

}

else if (dwReason == DLL_PROCESS_DETACH)

{

TRACE0( "EXTDLL.DLL Terminating!\n" );

// Terminate the library before destrUCtors are called

AfxTermExtensionModule( ExtDllDLL );

}

return 1; // ok

}

这一段代码含义晦涩,我们需要对其进行解读:

(1)上述代码完成MFC扩展DLL的初始化和终止处理;

(2)初始化期间所创建的 CDynLinkLibrary 对象使MFC扩展 DLL 可以将 DLL中的CRuntimeClass 对象或资源导出到应用程序;

(3)AfxInitExtensionModule函数捕捉模块的CRuntimeClass 结构和在创建 CDynLinkLibrary 对象时使用的对象工厂(COleObjectFactory 对象);

(4)AfxTermExtensionModule函数使 MFC 得以在每个进程与扩展 DLL 分离时(进程退出或使用AfxFreeLibrary卸载DLL时)清除扩展 DLL;

(5)第一条语句static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };定义了一个AFX_EXTENSION_MODULE类的静态全局对象,AFX_EXTENSION_MODULE的定义如下:

struct AFX_EXTENSION_MODULE

{

BOOL bInitialized;

HMODULE hModule;

HMODULE hResource;

CRuntimeClass* pFirstSharedClass;

COleObjectFactory* pFirstSharedFactory;

};

由AFX_EXTENSION_MODULE的定义我们可以更好的理解(2)、(3)、(4)点。

在资源编辑器中添加一个如图15所示的对话框,并使用MFC类向导为其添加一个对应的类CExtDialog,系统自动添加了ExtDialog.h和ExtDialog.cpp两个头文件。

图15 MFC扩展DLL中的对话框

修改ExtDialog.h中CExtDialog类的声明为:

class AFX_EXT_CLASS CExtDialog : public CDialog

{

public:

CExtDialog( CWnd* pParent = NULL );

enum { IDD = IDD_DLL_DIALOG };

protected:

virtual void DoDataExchange( CDataExchange* pDX );

DECLARE_MESSAGE_MAP()

};

这其中最主要的改变是我们在class AFX_EXT_CLASS CExtDialog语句中添加了“AFX_EXT_CLASS”宏,则使得DLL中的CExtDialog类被导出。

6.3 MFC扩展DLL的加载

6.3.1 隐式加载

我们在6.2工程所在的工作区中添加一个LoadExtDllDlg工程,用于演示MFC扩展DLL的加载。在LoadExtDllDlg工程中添加一个如图16所示的对话框,这个对话框上包括一个“调用DLL”按钮。

图16 MFC扩展DLL调用工程中的对话框

在与图16对应对话框类实现文件的头部添加:

// LoadExtDllDlg.cpp : implementation file

//

#include "..\ExtDialog.h"

#pragma comment( lib, "ExtDll.lib" )

而“调用DLL”按钮的单击事件的消息处理函数为:

void CLoadExtDllDlg::OnDllcallButton()

{

CExtDialog extDialog;

extDialog.DoModal();

}

当我们单击“调用DLL”的时候,弹出了如图15的对话框。

为提供给用户隐式加载(MFC扩展DLL一般使用隐式加载,具体原因见下节),MFC扩展DLL需要提供三个文件:

(1)描述DLL中扩展类的头文件;

(2)与动态链接库对应的.LIB文件;

(3)动态链接库.DLL文件本身。

有了这三个文件,应用程序的开发者才可充分利用MFC扩展DLL。

6.3.2 显示加载

显示加载MFC扩展DLL应使用MFC全局函数AfxLoadLibrary而不是WIN32 API中的LoadLibrary。AfxLoadLibrary 最终也调用了 LoadLibrary这个API,但是在调用之前进行了线程同步的处理。

AfxLoadLibrary 的函数原型与 LoadLibrary完全相同,为:

HINSTANCE AFXAPI AfxLoadLibrary( LPCTSTR lpszModuleName );

与之相对应的是,MFC 应用程序应使用AfxFreeLibrary 而非FreeLibrary 卸载MFC扩展DLL。AfxFreeLibrary的函数原型也与 FreeLibrary完全相同,为:

BOOL AFXAPI AfxFreeLibrary( HINSTANCE hInstLib );

假如我们把上例中的“调用DLL”按钮单击事件的消息处理函数改为:

void CLoadExtDllDlg::OnDllcallButton()

{

HINSTANCE hDll = AfxLoadLibrary( "ExtDll.dll" );

if(NULL == hDll)

{

AfxMessageBox( "MFC扩展DLL动态加载失败" );

return;

}

CExtDialog extDialog;

extDialog.DoModal();

AfxFreeLibrary(hDll);

}

则工程会出现link错误:

LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall CExtDialog::~CExtDialog(void)" (__imp_??1CExtDialogUAE@XZ)

LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall CExtDialog::CExtDialog(class CWnd *)" (__imp_??0CExtDialogQAE@PAVCWnd@Z)

提示CExtDialog的构造函数和析构函数均无法找到!是的,对于派生MFC类的MFC扩展DLL,当我们要在应用程序中使用DLL中定义的派生类时,我们不宜使用动态加载DLL的方法。

6.4 MFC扩展DLL加载MFC扩展DLL

我们可以在MFC扩展DLL中再次使用MFC扩展DLL,但是,由于在两个DLL中对于AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA宏的定义都是输出,这会导致调用的时候出现问题。

我们将会在调用MFC扩展DLL的DLL中看到link错误:

error LNK2001: unresolved external symbol ….......

因此,在调用MFC扩展DLL的MFC扩展DLL中,在包含被调用DLL的头文件之前,需要临时重新定义AFX_EXT_CLASS的值。下面的例子显示了如何实现:

//临时改变宏的含义“输出”为“输入”

#undef AFX_EXT_CLASS

#undef AFX_EXT_API

#undef AFX_EXT_DATA

#define AFX_EXT_CLASS AFX_CLASS_IMPORT

#define AFX_EXT_API AFX_API_IMPORT

#define AFX_EXT_DATA AFX_DATA_IMPORT

//包含被调用MFC扩展DLL的头文件

#include "CalledDLL.h"

//恢复宏的含义为输出

#undef AFX_EXT_CLASS

#undef AFX_EXT_API

#undef AFX_EXT_DATA

#define AFX_EXT_CLASS AFX_CLASS_EXPORT

#define AFX_EXT_API AFX_API_EXPORT

#define AFX_EXT_DATA AFX_DATA_EXPORT

6.5 MFC扩展DLL导出函数和变量

MFC扩展DLL导出函数和变量的方法也十分简单,下面我们给出一个简单的例子。

我们在MFC向导生成的MFC扩展DLL工程中添加gobal.h和global.cpp两个文件:

//global.h:MFC扩展DLL导出变量和函数的声明

extern "C"

{

int AFX_EXT_DATA total; //导出变量

int AFX_EXT_API add( int x, int y ); //导出函数

}

//global.cpp:MFC扩展DLL导出变量和函数定义

#include "StdAfx.h"

#include "global.h"

extern "C" int total;

int add(int x,int y)

{

total = x + y;

return total;

}

编写一个简单的控制台程序来调用这个MFC扩展DLL:

#include

#include 单击此处下载本工程)。

我们知道static控件所对应的CStatic类不具备设置背景和文本颜色的接口,这使得我们不能在对话框或其它用户界面上自由灵活地修改static控件的颜色风格,因此我们需要一个提供了SetBackColor和SetTextColor接口的CStatic派生类CMultiColorStatic。

这个类的声明如下:

class AFX_EXT_CLASS CMultiColorStatic : public CStatic

{

// Construction

public:

CMultiColorStatic();

virtual ~CMultiColorStatic();

// Attributes

protected:

CString m_strCaption;

COLORREF m_BackColor;

COLORREF m_TextColor;

// Operations

public:

void SetTextColor( COLORREF TextColor );

void SetBackColor( COLORREF BackColor );

void SetCaption( CString strCaption );

// Generated message map functions

protected:

afx_msg void OnPaint();

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类添加“设置文本颜色”接口

void CMultiColorStatic::SetTextColor( COLORREF TextColor )

{

m_TextColor = TextColor; //设置文字颜色

}

//为CMultiColorStatic类添加“设置背景颜色”接口

void CMultiColorStatic::SetBackColor( COLORREF BackColor )

{

m_BackColor = BackColor; //设置背景颜色

}

//为CMultiColorStatic类添加“设置标题”接口

void CMultiColorStatic::SetCaption( CString strCaption )

{

m_strCaption = strCaption;

}

//重画Static,颜色和标题的设置都依靠于这个函数

void CMultiColorStatic::OnPaint()

{

CPaintDC dc(this); // device context for painting

CRect rect;

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"

#pragma comment ( lib, "ColorStatic.lib" )

// CCallDllDlg dialog

class CCallDllDlg : public CDialog

{

public:

CCallDllDlg(CWnd* pParent = NULL); // standard constructor

enum { IDD = IDD_CALLDLL_DIALOG };

CMultiColorStatic m_colorstatic; //包含一个CMultiColorStatic的实例

protected:

virtual void DoDataExchange(CDataExchange* pDX);//DDX/DDV support

HICON m_hIcon;

// Generated message map functions

//{{AFX_MSG(CCallDllDlg)

virtual BOOL OnInitDialog();

afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

afx_msg void OnPaint();

afx_msg HCURSOR OnQueryDragIcon();

afx_msg void OnRedButton();

afx_msg void OnBlueButton();

afx_msg void OnGreenButton();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

下面是这个类中与使用CMultiColorStatic相关的主要成员函数:

void CCallDllDlg::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

}

BOOL CCallDllDlg::OnInitDialog()

{

// TODO: Add extra initialization here

// 初始static控件的显示

m_colorstatic.SetCaption("最开始为黑色");

m_colorstatic.SetTextColor(RGB(0,0,0));

return TRUE; // return TRUE unless you set the focus to a control

}

//设置static控件文本颜色为红色

void CCallDllDlg::OnRedButton()

{

m_colorstatic.SetCaption( "改变为红色" );

m_colorstatic.SetTextColor( RGB( 255, 0, 0 ) );

Invalidate( TRUE ); //导致发出WM_PAINT消息

}

//设置static控件文本颜色为蓝色

void CCallDllDlg::OnBlueButton()

{

m_colorstatic.SetCaption( "改变为蓝色" );

m_colorstatic.SetTextColor( RGB( 0, 0, 255 ) );

Invalidate( TRUE ); //导致发出WM_PAINT消息

}

//设置static控件文本颜色为绿色

void CCallDllDlg::OnGreenButton()

{

m_colorstatic.SetCaption( "改变为绿色" );

m_colorstatic.SetTextColor( RGB(0,255,0) );

Invalidate( TRUE ); //导致发出WM_PAINT消息

}

至此,我们已经讲解完成了所有类型的动态链接库,即非MFC DLL、MFC规则DLL和MFC扩展DLL。下一节将给出DLL的三个工程实例,与读者朋友们共同体会DLL的应用范围和使用方法。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有