资源MFC DLL的制作[在DLL中封装MFC对话框]

38 篇文章 1 订阅

1. 首先在MFC DLL 的文件中函数中添加 PIMSHQERON001100.DLL

BOOL CPIMSHQERON001100App::InitInstance()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
COleObjectFactory::RegisterAll();
CWinApp::InitInstance();


AfxEnableControlContainer();
CoInitialize(NULL);



//if(!AfxOleInit())
//{
// return FALSE;
//}


return TRUE;
}


2. 在调用窗体时

//资源切换,无此宏对话框创建不成功
AFX_MANAGE_STATE(AfxGetStaticModuleState());//需要这句。


EditReleaseOnLineFile *editRelFile = new EditReleaseOnLineFile();//(pSession, pResultElementPtr);


editRelFile->DoModal();


3. 对话框内部有时没有OnInitDialog() 函数,请手动添加

// 生成的消息映射函数
virtual BOOL OnInitDialog();

BOOL EditReleaseOnLineFile::OnInitDialog()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxEnableControlContainer();


CDialog::OnInitDialog();
CoInitialize(NULL);


//if(!AfxOleInit())
//{
// return FALSE;
//}
return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}


附其他方式:

二.建立一个可以包含MFC对话框的DLL 
树形控件仅能以子窗口的形式出现,它要依附对话框这样的父窗口。 可以在UG二次开发的DLL项目中直接添加对话框资源和对话框类来实现,手段虽不怎么高明,但能实现所需的功能。但我觉得最好将与UG没有多大关系的功能分离出来,作为一个模块单独实现,这也是软件工程所鼓励的方式。 

本文的方法是首先做一个DLL,让包含一个MFC对话框,这个DLL可以在UG二次开发的项目中被加载。被包含在DLL中的对话框通常称为资源对话框。 

下面为建立可以包含对话框资源的DLL的过程。 
1.建一个MFC Regular DLL项目 . 
VS.net(没找到英文版,菜单只能给出中文名称)的建立一个DLL的过程如下: 
[1]菜单 文件→新建→项目,在弹出的项目对话框的左栏,选择Visual C++项目,在右栏选择MFC DLL。然后在下面的文本框中输入项目的名字,确定,进入MFC DLL向导。 
[2]MFC DLL向导,在"应用程序设置"中,选择'使用共享MFC DLL的规则DLL",完成向导设置后,生成一个空的MFC DLL项目。 
[3]菜单:项目→添加资源,在添加资源对话框中,选择"Dialog",然后点击按钮"新建"。VS.net会自动切换到资源视图界面,删去默认的"OK"和"CANCEL" 按钮。 
[4]将默认的对话框ID修改成你能记得住的名字。 
[5]添加对话框类CTreeDlg。 
//-------------------- 
以上过程都是IDE在工作,下面就要动脑,动手了。 
//------------------- 
[6] 建立输出函数ShowTreeDlg 
DLL是无法自动进入内存开始运行的,要被其它可执行文件的加载才可以。对话框是在DLL中创建的,我们期望在UG二次开发的项目中,在某个UIStyler控件的触发下弹出这个DLL中创建的对话框。而常规的MFC DLL是无法导出MFC对象给其它应用程序使用的,只能通过输出函数来做。 

向工程中添加两个文件ExportFunc.h和ExportFunc.CPP,我打算将输出函数的声明和定义统一放在这两个文件中,便于管理。代码实现如下: 
//-------------------------------------------- 
//ExportFunc.h, 
//声明欲输出的函数 
//------------------------------------------- 
#ifndef _EXPORTFunc_H 
#define _EXPORTFunc_H 
#ifdef _cplusplus 
extern "C"{ 
#endif 
void ShowTreeDlg(HWND hMainWnd); 
#ifdef _cplusplus 

#endif 
#endif 

//-------------------------------------------- 
//ExportFunc.cpp文件 
//定义输出函数 
//------------------------------------------- 
// 
//功能:DLL的输出函数,当其他应用程序加载该DLL后,调用这个函数,可以显示该DLL内建的对话框 
//输入参数1:HWND hMainWnd,对话框父窗口的句柄 
// 
void ShowTreeDlg(HWND hMainWnd) 

  AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
  CTreeDlg *pTreeDlg=new CTreeDlg; 
  CWnd * pMainWnd=CWnd:: FromHandle(hMainWnd); 
  ASSERT(pMainWnd); 
  BOOL retValue=pTreeDlg->Create(IDD_TREE,pMainWnd); 
  if(!retValue) 
  { 
    AfxMessageBox("创建包含树列表控件的对话框失败了!"); 
  } 
  pTreeDlg->ShowWindow(SW_SHOW); 

下面是模块定义文件的内容,通常我们是使用_declspec(dllexport)直接修饰输出的函数,这样导出的接口很容易就被查看DLL文件的工具观察到,保密性不够好。为了向外界隐藏你的DLL对外接口的名称,只有def文件可以做。 
; DlgWithTreeCtrl.def : 声明 DLL 的模块参数。 

LIBRARY "DlgWithTreeCtrl" 

EXPORTS 
    ; 此处可以是显式导出 
   ShowTreeDlg @1 NONAME

 

知识点1:MFC模块状态的切换 
上贴,在ExportFunc.CPP中,定义输出函数ShowTreeDlg时,函数体开头一句是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; 
       pDlg->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());

 

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

任务:测试DlgWithTreeCtrl.dll 
所需的文件:ExportFunc.h,DlgWithTreeCtrl.lib,DlgWithTreeCtrl.dll。 

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

[2]Win32控制台程序向导中,在"应用程序设置"中,选择'控制台应用程序",没有必要让你的控制台程序支持MFC,其它都采用默值,完成向导设置。 

[3]如果此时testDll项目是debug模式的,将DlgWithTreeCtrl.lib和DlgWithTreeCtrl.dll拷贝到testDll目录中的debug文件夹下。 

[4]testDll的主程序文件testDll.cpp中内容如下: 
//--------------------------- 
//testDll.cpp 
//-------------------------- 
#include "stdafx.h" 
#include "..\DlgWithTreeCtrl\ExportFunc.h" 
#pragma comment(lib, "..\\DlgWithTreeCtrl\\release\\DlgWithTreeCtrl.lib") 

void main(void) 

  ShowTreeDlg(NULL); 


[5]编译连接,运行。运行结果是一个控制台窗口出现,然后一个对话框一闪就不见了。呵呵,对话框确实被显示了,只是控制台程序瞬间就结束了,所以对话框就被自动销毁了。可以在main函数中加上一句: 
MessageBox(NULL,"stop",NULL,MB_OK); 
让控制台程序不那么快就消亡。 

最终结果如下图:

 

四. 还需要继续考虑的问题 
我在DlgWithTreeCtrl.dll中做的那个输出函数,new了一个对话框的对象,程序中从始至终都没有释放所new的(堆)空间。会不会内存泄露?这不是一个小问题。换个说法:MFC的窗口对象是怎样从系统中清除的呢?要弄清楚这个问题,必须先搞清楚窗口对象的成分。 

一个MFC窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在m_hWnd成员中的HWND(窗口句柄),二是窗口对象本身是一个C++对象。要删除一个MFC窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。也就是窗口的C++对象的生存期要比窗口长。 

[1]窗口的删除 
删除窗口最直接方法是调用CWnd:: DestroyWindow或全局API函数:: DestroyWindow,前者封装了后者的功能。前者不仅会调用后者,而且会使成员m_hWnd保存的HWND无效(NULL)。如果DestroyWindow删除的是一个父窗口或拥有者窗口,则该函数会先自动删除所有的子窗口或被拥有者,然后再删除父窗口或拥有者。在一般情况下,在程序中不必直接调用DestroyWindow来删除窗口,因为MFC会自动调用DestroyWindow来删除窗口。例如,当用户退出应用程序时,会产生WM_CLOSE消息,该消息会导致MFC自动调用CWnd:: DestroyWindow来删除主框架窗口。当用户在对话框内按了OK或Cancel按钮时,可调用CWnd:: DestroyWindow来删除对话框及其控件。 

[2]窗口对象的删除 
窗口对象本身的删除则根据对象创建方式的不同,分为两种情况。在MFC编程中,会使用大量的窗口对象,有些窗口对象以变量的形式嵌入在别的对象内或以局部变量的形式创建在堆栈上,有些则用new操作符创建在堆中。 

对于一个以变量形式创建的窗口对象,程序员不必关心它的删除问题,因为该对象的生命期总是有限的,若该对象是某个对象的成员变量,它会随着父对象的消失而消失,若该对象是一个局部变量,那么它会在函数返回时被清除。 

对于一个在堆中动态创建的窗口对象,其生命期却是任意长的。因为用new在堆中创建对象,就不能忘记用delete删除对象。有些MFC窗口对象具有自动清除的功能。不需要程序员显示的调用delete来删除它。 
------------------------------------------------------------------------------------------------------ 
------------------------------------------------------------------------------------------------------ 
[3]对于我new的那个非模态的对话框,该如何销毁呢? 
这个问题缠绕我好几天,至今为止还是没有解决的。经测试,发现上面来自网上的和来自MSDN的资料说的不正确。 

例如这句话:"用户退出应用程序时,会产生WM_CLOSE消息,该消息会导致MFC自动调用CWnd:: DestroyWindow来删除主框架窗口。" 

经过测试,当我点了对话框窗口的系统菜单上的"关闭"按钮后,产生WM_CLOSE消息,而它并没有调用CWnd:: DestroyWindow来销毁窗口!!!!!! 

这个问题最终是在这里得到解决的,去这里看看: 
http://community.csdn.net/Expert/topic/4025/4025795.xml?temp=.989773

 


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值