当前位置:C++技术网 > 资讯 > 如何在DLL中封装MFC对话框

如何在DLL中封装MFC对话框

更新时间:2015-06-25 16:51:49浏览次数:1+次

基础知识

    要知道窗口对象、应用实例事件、消息、消息映射和怎样实现消息映射。对于DLL,知道怎样输出一些函数,怎样在应用程序中加载一个DLL,并使用它们提供的接口。所谓的接口,就是DLL提供的函数,可以让我们使用。 
  • 控件通知消息(Notification message)
    控件中发生了一些事件,是在控件的父窗口中响应事件,而不是在控件本身响应的。程序实现时,就相应的将这些事件的处理统统放在控件的父窗口对象的消息成员函数中。控件通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及树状视图、列表视图等公共控件。例如,单击或双击一个控件、在控件中选择文本、操作控件的滚动条都会产生通知消息。
  • 采用的DLL格式
    由于我们只是想使用DLL导出的对话框资源,而且还想着有可能在非MFC环境中使用该对话框资源,规则的MFC DLL可以胜任,就没必要使用MFC扩展DLL了,另外如果使用了扩展的MFC DLL,那么你的应用程序也必须使用MFC库。
  • 建立一个可以包含MFC对话框的DLL  
控件仅能以子窗口的形式出现,它要依附对话框这样的父窗口。 可以在DLL项目中直接添加对话框资源和对话框类来实现。

制作DLL,让其包含一个MFC对话框

以下是建立DLL的过程:
VS中建立一个DLL的过程如下:  
  1. 菜单->文件->新建->项目,在弹出的项目对话框的左栏,选择Visual C++项目,在右栏选择MFC DLL。然后在下面的文本框中输入项目的名字,确定,进入MFC DLL向导。  
  2. MFC DLL向导,在"应用程序设置"中,选择'使用共享MFC DLL的规则DLL",完成向导设置后,生成一个空的MFC DLL项目。注意,共享的Dll需要使用的计算机中安装有MFC库,否则无法运行。要在没有MFC库的计算机中运行,请选择静态DLL版本。  
  3.  菜单:项目->添加资源,在添加资源对话框中,选择"Dialog",然后点击按钮"新建"。VS会自动切换到资源视图界面,删去默认的"OK"和"CANCEL" 按钮。  
  4. 的对话框ID修改为合适的ID号。  
  5. 添加话框类CMyDllDlg。
  6. 建立输出函数ShowMyDllDlg()
说明:DLL是无法自动进入内存开始运行的,要被其它可执行文件的加载才可以。对话框是在DLL中创建的,而常规的MFC DLL是无法导出MFC对象给其它应用程序使用的,只能通过输出函数来做。可以参考一下《动态链接库dll创建和使用相关详细说明》。

导出函数

向项目中添加两个文件ExportFunc.h和ExportFunc.CPP,将输出函数的声明和定义统一放在这两个文件中,便于管理。代码实现如下:  
//--------------------------------------------  
//ExportFunc.h,  
//声明欲输出的函数  
//-------------------------------------------  
#ifndef _EXPORTFunc_H  
#define _EXPORTFunc_H  
#ifdef _cplusplus  
extern "C"{  
#endif  
void ShowMyDllDlg(HWND hMainWnd);  
#ifdef _cplusplus  
}  
#endif  
#endif  
  
//--------------------------------------------  
//ExportFunc.cpp文件  
//定义输出函数  
//-------------------------------------------  //////////////////////////////////////////////////////
//功能:DLL的输出函数,当其他应用程序加载该DLL后,调用这个函数,可以显示该DLL内建的对话框  
//输入参数1:HWND hMainWnd,对话框父窗口的句柄  
///////////////////////////////////////////////  
void ShowMyDllDlg(HWND hMainWnd)  
{  
AFX_MANAGE_STATE(AfxGetStaticModuleState());  
CMyDllDlg *pTreeDlg=new CMyDllDlg;  
CWnd * pMainWnd=CWnd:: FromHandle(hMainWnd);  
ASSERT(pMainWnd);  
BOOL retValue=CMyDllDlg->Create(IDD_DLG,pMainWnd);  
// - IDD_DLG就是添加的对话框的ID
if(!retValue)  
{  
AfxMessageBox("创建包含树列表控件的对话框失败了!");  
}  
CMyDllDlg->ShowWindow(SW_SHOW);  
}  
下面是模块定义文件的内容,通常我们是使用_declspec(dllexport)直接修饰输出的函数,这样导出的接口很容易就被查看DLL文件的工具观察到,保密性不够好。为了向外界隐藏你的DLL对外接口的名称,只有def文件可以做。  
DlgWithCtrl.def : 声明 DLL 的模块参数。  
  
LIBRARY "DlgWithCtrl"  
  
EXPORTS  
此处可以是显式导出  
ShowMyDllDlg @1 NONAME

详细说明:MFC模块状态的切换
在ExportFunc.CPP中,定义输出函数ShowMyDllDlg时,函数体开头一句是AFX_MANAGE_STATE(AfxGetStaticModuleState());
对MFC DLL不熟悉的朋友在使用MFC的库时,常常会丢了这句,以致出现一些错误。了解一下是非常有帮助的。

1. 模块的定义:一段可执行的程序,其程序代码,数据,资源被加载到内存中(这种加载可以是操作系统来做,也可以是已被加载到内存中的模块来做),这段程序进入内存后,系统会为之建立一个数据结构来管理,这个数据结构在WINDOWS中,就是PE文件头。一个活动在内存中的EXE文件是不是模块?一个活动在内存中的DLL文件是不是模块?它们都是。
  
2. MFC模块:使用了MFC库的模块。
  
3. MFC模块的状态:是一个数据结构,里面存了许多与模块相关的数据,DLL中所包含的对话框是存于资源模板中的,而资源模板是存于这个MFC模块的状态数据结构中的。了解这一点就可以了。
  
4. 一个MFC程序运行时,默认状态下,当它需要资源时,比如它需要一个对话框,那么它会从自身的模块状态中找到资源模板,然后从资源模板中找到相应的对话框,它是怎么找到自己所需要的对话框模板的呢?根据对话框ID,这是一个整型数。
然而,当一个MFC的应用程序在运行时,可能要加载多个MFC模块。譬如程序A.exe在运行时,加载了B.dll。在B.dll中提供了一个输出函数ShowDlg,函数体定义如下:
//-------------------------------------------------------------------------
//该函数显示一个非模态对话框,写法极简,仅作示例
//-------------------------------------------------------------------------
void ShowDlg(void)
{
    CDlg *pDlg = new CDlg;
    Create(IDD_DLG,NULL);
    pDlg->ShowWindow(SW_SHOW);
}

我们可以看到,B.dll中肯定是存在一个ID为IDD_DLG的对话框模板的,按照上面所说的,这个对话框模板被存到了B.dll的状态模块中了。当A.exe中调用这个输出函数时,运行到pDlg->Create(IDD_DLG,NULL)时,它就会去自己的模块状态中去查找标号为IDD_DLG的对话框模板,结果有可能找得到,也有可能找不到。假如A.exe的模块状态中有一个对话框模板的ID恰好等于IDD_DLG时,就能找到,但弹出的对话框并不是你在B.dll中定义的那个。当A.exe的模块状态中没有ID恰好等于IDD_DLG的对话框时,那就是找不到,程序会报错。
  
5. 怎么解决
在第4个说明中,当程序A调用B.dll中的输出函数ShowDlg时,也就是说程序A进入了函数ShowDlg。倘若在ShowDlg的入口点处,把应用程序默认的自身模块状态切换成B.dll的模块状态,这样再查找标号为IDD_DLG的对话框时,就会从B.dll的模块状态中查找了,结果就能找到。这个切换语句就是在ShowDlg函数体的第一句处添加:AFX_MANAGE_STATE(AfxGetStaticModuleState());

6.添加其他控件在窗口上
如果要在DLL的窗口中显示其他的控件,只要在添加的窗口资源中添加控件,并在窗口消息响应成员函数中处理控件消息即可。这里就和普通的控件处理是一样的。

测试这个包含对话框资源的DLL
由于这个Dll是采用MFC Regular DLL的格式写的,MSDN中说它可以被任何WIN32程序调用,要测试它,简单起见,只需写个Console程序来测即可。
  
任务:测试DlgWithCtrl.dll
所需的文件:ExportFunc.h,DlgWithCtrl.lib,DlgWithCtrl.dll。
  

测试DLL

1. 菜单->文件->新建->项目,在弹出的项目对话框的左栏,选择Visual C++项目,在右栏选择Win32控制台项目。然后在下面的文本框中输入项目的名字testDll,确定,进入Win32控制台程序向导。

2. Win32控制台程序向导中,在"应用程序设置"中,选择'控制台应用程序",没有必要让你的控制台程序支持MFC,其它都采用默值,完成向导设置。
  
3. 如果此时testDll项目是debug模式的,将DlgWithCtrl.lib和DlgWithCtrl.dll拷贝到testDll目录中的debug文件夹下。
  
4. testDll的主程序文件testDll.cpp中内容如下:
//---------------------------
//testDll.cpp
//--------------------------
#include "stdafx.h"
#include "..\DlgWithCtrl\ExportFunc.h"
#pragma comment(lib, "..\\DlgWithCtrl\\release\\DlgWithCtrl.lib")  
  
void main(void)
{
    ShowTreeDlg(NULL);
}
5. 编译连接,运行。
运行结果是一个控制台窗口出现,然后一个对话框一闪就不见了。呵呵,对话框确实被显示了,只是控制台程序瞬间就结束了,所以对话框就被自动销毁了。可以在main函数中加上一句:
MessageBox(NULL,"stop",NULL,MB_OK);
让控制台程序不那么快就消亡。