1.CString 转换 char *
CString str = "123";
// 转换为char指针
char *p = str.GetBuffer(str.GetLength());
// 转换为int变量
int i = atoi(p);
cout << i << endl;
// 转换为float变量
str = "123.32";
float f = atof(str.GetBuffer(str.GetLength()));
cout << f << endl;
Int,Float,Char *,CString之间的转换
Int 转CString :
int n=1;
CString str;
str.Format("%d",n);
CString 转 Int
CString str="1";
int n=atoi(str.GetBuffer(0));
char * 转 CString:
char sz[128];
CString str;
str.Format("%s",sz);
CString 转 char *:
CString str;
int nLength=str.GetLength();
char * sz=new char[nLength];
sz=str.GetBuffer(0);
2.MFC中EDIT控件值的初始化(如:GetDlgItem(IDC_SET_NAME)->SetWindowText(temp);)必须在OnInitDialog()函数中完成
3.
正好这里有些现成的
位图按钮的实现方法: 位图必须用.bmp 格式的
首先,我们创建一个基于对话框的应用程序CmyDialog ;
Ι.MFC的CBitmapButton类,这也是最简单的功能最强的位图按钮。我们可以采取如下的步骤:
需定义CBitmapButton 对象与CButton一一对应.
1. 在创建按钮资源IDC_BUTTON时选择 owner draw (自己画);
2. 在对话框.h文件内定义位图按钮对象 CBitmapButton mybutton;
3. 在对话框.c文件内创建 BOOL Dialog::OnInitDialog() {}
如果已有就不必创建;
4.一种方法:
mybutton.LoadBitmaps(IDB_BITMAP1, IDB_BITMAP1, IDB_BITMAP1,IDB_BITMAP1);
mybutton.SubclassDlgItem(IDC_hangup, this);
mybutton.SizeToContent();
另一种方法:
在Oninitdialog()中VERIFY(mybutton.AutoLoad(IDC_BUTTON,this));
创建位图,其资源ID应与按钮字符相同为:"字符U","字符D","字符X"(分别对应释放,按下,无效. 引号也要打!)
使用CBitmapButton;
在Oninitdialog()调用
m_btnSearch.LoadBitmaps(IDB_SEARCHU, IDB_SEARCHD, IDB_SEARCHF, IDB_SEARCHX);
Ⅱ.使用图标制作按钮
1. 打开ICON按钮的属性页,在Style中选中Icon 。
2. 在对话框类的头文件中定义成员变量(使用ClassWizard加入这个成员变量)
CButton m_ IconBtn;//对应于图标按钮
3. 创建相应的图标或者位图资源:
图标资源:IDI_ICONBUTTON
4.在初始化中加入如下代码:
…
//对应于图标按钮 图标必须是.ico格式的
HICON hIcon=AfxGetApp()->LoadIcon(IDI_ICONBUTTON);
m_IconBtn.SetIcon(hIcon);
…
重新编译运行我们的程序,奇妙的图像按钮呈现在我们的眼前了。
Ⅲ.使用位图制作按钮 位图必须用.bmp 格式的
1. 打开BITMAP按钮的属性页,在Style中选中Bitmap。
2. 对话框类的头文件中定义成员变量(不用ClassWizard加入这个成员变量)
CBitmapButton mybutton;
3.创建位图资源:
位图资源:(IDB_BITMAP1))
4.在初始化中加入如下代码:
//对应于位图按钮
…
HBITMAP hBmp=::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP1));
((CButton*)GetDlgItem(IDC_hangup))->SetBitmap(hBmp);
4.
1.mixerGetNumDevs获取混音设备数量
2.循环获取设备信息mixerOpen/mixerGetLineInfo
3.根据获得的MIXERLINE中dwComponentType判断类型来确定是线入/麦克风
MIXERLINE_COMPONENTTYPE_SRC_LINE/MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
4.mixerSetControlDetails设置音量
谢谢BLsoft(网龙)
MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE 但是这个是设置"音量属性"里的麦克风
我希望能设置"录音属性"里的麦克风
// 打开Mixer设备
mixerOpen( &m_hMixer, 0或1, 0, 0, MIXER_OBJECTF_MIXER );
MIXERLINE MixerLine;
// 结构体的大小
MixerLine.cbStruct = sizeof(MixerLine);
// 录制设备总线
MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
// 得到录制总线中的连接数
mixerGetLineInfo( (HMIXEROBJ)m_hMixer, &MixerLine,
MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE );
// 将连接数保存
DWORD dwConnections = MixerLine.cConnections;
// 准备获取麦克风设备的ID
DWORD dwLineID = 0;
for ( DWORD i = 0; i < dwConnections; i++ )
{
// 枚举每一个设备,当Source的ID等于当前的迭代记数
MixerLine.dwSource = i;
// 根据SourceID获得连接的信息
mr = mixerGetLineInfo( (HMIXEROBJ)m_hMixer, &MixerLine,
MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_SOURCE );
// 判断函数执行错误
if ( mr != 0 )
{
break;
}
// 如果当前设备类型是麦克风,则跳出循环。
if ( MixerLine.dwComponentType ==
MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE )
{
dwLineID = MixerLine.dwLineID;
break;
}
}
// 如果没有找到,返回失败。
if ( dwLineID == 0 )
{
return FALSE;
}
5. VC中将程序前端显示的方法 收藏
来源:http://blog.ednchina.com/bromi/18716/message.aspx
调用函数:
BOOL SetWindowPos(
HWND hWnd, // handle to window
HWND hWndInsertAfter, // placement-order handle
int X, // horizontal position
int Y, // vertical position
int cx, // width
int cy, // height
UINT uFlags // window-positioning flags
);
例如:
void CSwitcherDlg::OnCheck1()
{
// TODO: Add your control notification handler code here
UpdateData(true);
if (m_check_1)
{
::SetWindowPos(this->m_hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); // top view
}
else
{
::SetWindowPos(this->m_hWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); // normal view
}
}
AfxGetMainWnd()->EnableWindow(FALSE);//将主窗口Disable,显示出的非模态窗口就变成模态的了
模态窗口原理及注意事项--http://www.alisdn.com/wordpress/?p=53
Posted on 2009-09-26 13:19 懒人ABC 阅读(79) 评论(0) 编辑 收藏 网摘 所属分类: windows原理
前言
在开发Windows引用程序的时候,在一些需要用户确认,或者提示用户注意的场合,经常使用模态对话框,或者叫模态窗口。在绝大多数情况下,模态窗口给开发人员带来了极大的便利,并且在某些应用上有不可替代的优势。然而凡事有利必有弊,如果不正确地使用模态窗口,却有可能带来某些严重问题,甚至可能引起程序崩溃。要想知道为什么模态窗口可能带来某些严重问题,就必须首先了解模态窗口的实现原理。因此本文将首先介绍模态窗口实现原理,然后分析为什么会带来问题。
原理
知道了原理,一切就可迎刃而解。了解了原理,就可以知道,模态窗口并不是Windows特有的,而是可以在任何一个GUI系统中实现出来,包括手机上。
因为Windows上的模态对话框为众人所知,因此本文的例子都是指Windows上的,并且有时候会特指是MFC的。
众所周知,当模态窗口被打开之后,正常的流程会暂时挂起,或者通俗一点说,程序停住了,直到模态窗口关闭才会继续执行。例如下面这段代码:
CInputDialog dlg;
if(dlg.DoModal() == IDOK)
{
// 执行按了确定按钮退出的流程
}
else
{
// 执行通过别的方式退出的流程,例如按了取消按钮
}
// 继续执行
在这段代码里,在CInputDialog窗口关闭之前,注释部分的代码是不会得到执行的。
接下来请先思考一个问题,为什么调用了dlg.DoModal()之后,程序会停住呢?
首先,不可能是线程被挂起,因为一般情况,只有一个主线程,如果线程挂起,那就什么也做不了了,但显然模态窗口弹出来之后还是可以做很多事情的。
其次,也不可能是用类似于Sleep之类的函数,让程序等待,和线程挂起一样。
如果我们了解Windows应用程序的运行的原理,了解消息分发的机制,就可以知道,UI线程有一个消息循环,通过GetMessage之类的函数获取消息,并且分发。如果没有这个消息循环,整个窗口系统就无法正常工作。很显然,当有模态窗口打开的时候,整个窗口系统还是正常工作的,因此可以确定,此时消息循环一定还在正常运行着。这个消息循环在哪里呢?因为当模态对窗口弹出来之后,程序就暂停了,相当调用模态窗口的函数一直没有返回,那么也就没有机会再进入缺省消息循环了,这到底是怎么回事呢?福尔摩斯经常说:"除去不可能的剩下的即使再不可能,那也是真相。"基于这个道理,真像只有一个,就是模态窗口内部有一个消息循环,负责消息的接收和转发。
为了证明这个说法,可以做个试验,弹出一个模态对话框,并设置合适的断点,查看堆栈。
使用DialogBox(NULL, MAKEINTRESOURCE(IDD_MAINDLG), m_hWnd, DialogProc);语句弹出对话框,并且在DialogProc里设置一个合适的断点,我们可以在堆栈中看到这样的信息:
ZK.exe!CMainDlg::DialogProc(HWND__ * hwndDlg=0×000411e0, unsigned int uMsg=0×00000201, unsigned int wParam=0×00000001, long lParam=0×003a009c) 行90 C++
user32.dll!_InternalCallWinProc@20() + 0×23 字节
user32.dll!_UserCallDlgProcCheckWow@32() + 0xa9 字节
user32.dll!_DefDlgProcWorker@20() + 0×7f 字节
user32.dll!_DefDlgProcW@16() + 0×22 字节
user32.dll!_InternalCallWinProc@20() + 0×23 字节
user32.dll!_UserCallWinProcCheckWow@32() + 0xb3 字节
user32.dll!_DispatchMessageWorker@8() + 0xe6 字节
user32.dll!_DispatchMessageW@4() + 0xf 字节
user32.dll!_IsDialogMessageW@8() - 0xeaa7 字节
user32.dll!_DialogBox2@16() + 0xc0 字节
user32.dll!_InternalDialogBox@24() + 0xb6 字节
user32.dll!_DialogBoxIndirectParamAorW@24() + 0×36 字节
user32.dll!_DialogBoxParamW@20() + 0×3f 字节
ZK.exe!CMainDlg::OnOK(unsigned short __formal=0×0000, unsigned short wID=0×0001, unsigned short __formal=0×0000, unsigned short __formal=0×0000) 行98 + 0×1d 字节 C++
上面的堆栈信息中,红色加粗的函数是API函数IsDialogMessage,这个函数的第二个参数是LPMSG lpMsg,这个正是从GetMessage返回的当前消息的结构体。可以想象,在DialogBox函数内部的实现里,在调用IsDialogMessage之前,必定先通过GetMessage之类的函数,从消息队里返回了当前的消息了。
到了这里,我们基本可以确定,在模态窗口内部,也实现了一个消息循环,真是这个消息循环接管了线程中缺省的消息循环,使整个窗口系统能继续正常的工作。同时由于消息循环其实也是一个有退出条件的死循环,因此到这个循环结束之前(一般是关闭了模态窗口),模态窗口后面的代码是不会继续执行的。
理解了模态窗口的原理,就可以在任何支持消息队列的GUI系统中,加入模态窗口的机制,这会减少很多开发工作。例如很多手机平台不支持模态窗口,开发一些需要用户确认的功能就比较麻烦,其实完全可以加入模态窗口,简化开发。
注意事项
模态窗口极大地简化了一些需要和用户交互的操作,好处显而易见。但这里还是要指出一些需要注意的地方,否则使用的时候很可能会出问题。
影响PreTranslateMessage机制
在使用MFC,WTL等进行开发的时候,经常用到PreTranslateMessage机制,这个机制可以让我们在消息被派发之前先做一些事情。很多人以为PreTranslateMessage是Windows本身支持的,其实不然。PreTranslateMessage是MFC和WTL自己引入的一个概念,完全是和Windows无关的。在MFC和WTL的消息循环中,这两个库的设计者在消息分发之前,人为的加了一些代码,使得整个架构支持这一套机制。
正是如此,如果在正常的流程中弹出了模态窗口,就会使正常的PreTranslateMessage机制失效。因为模态窗口中已经包含了一个消息循环,接管了线程中缺省的消息循环。而这个消息循环是在DialogBox这个API函数中执行的,显然不可能再有PreTranalateMessage机制了。
为了解决这一问题,只有让模态窗口也使用和UI线程相同的消息循环,MFC正是这么做的。在MFC中,对话框类的DoModal函数,并不是调用DialogBox函数,而是直接使用CreateWindows创建一个非模态窗口,在窗口创建成功之后再调用MFC自己的消息循环,这样就可以让PreTranslateMessage继续生效。同时在窗口创建出来之后,必须再做一些别的操作,使这个模态窗口的父窗口失效(一般直接把窗口Disable掉)。同时消息循环里有合适的退出条件,并有恢复现场的一些操作,具体可以查看MFC的DoModal函数。
WTL到目前为止,貌似暂时还没有一个合适的方案来解决这个问题。事实上WTL的PreTranslateMessage机制实现的其实是有点问题的,或许以后会在这方面做一定的增强。
可能导致崩溃
这是一个严重问题,在条件合适的情况下,这个崩溃是必然的。
因为模态窗口弹出来之后,模态窗口后面的代码在窗口关闭之前将不会得到执行。然而此时整个窗口是在正常运行的,对于一些极端的情况,是极有可能造成崩溃的。下面看一个例子:
void CTestDlg::OnOK()
{
CInputDialog dlg;
If(dlg.DoModal() == IDOK)
{
m_nValue = dlg.GetValue();
UpdateData(FALSE);
}
}
这是一段典型的MFC代码,在绝大多数情况下,不会有任何问题。但是由于模态窗口弹出的时候,只是父窗口不能操作,但别的窗口完全还能正常运行,这时候就非常有可能由于某种原因,CTestDlg类已经销毁了,而CInputDialog却不知道,还在继续执行,结果到了IDOK之后,对CTestDialog类的成员变量m_nValue赋值,就会出现崩溃了。
这个问题,如果在多线程的情况下,将会更加严重。因为在多线程的情况下,将会有更加多的不可预料的因素,所以使用的时候要更加小心。
vc的模态对话框和非模态对话框 大 | 中 | 小
http://www.lewensky.cn/read.php/214.htm
23:35
Lewen
MFC/VC++ From: beginner的博客
Generally Speaking
(1)创建一非模态对话框
CMyDlg* pMainWnd = new CMyDlg;
pMainWnd->Create();
(2)创建一模态对话框
CMyDlg dlg;
dlg.Domdal();
非模态的对话框是独立的,就是说和主程序可以同时交换数据,而模态的对话框则只能和自己交换。换句话说,模态对话框很霸道,你开了它就无法在同一个应用程序中的其他地方干活了,比如那个经常用的openfile dialog。非模态对话框就很随和,开了它你还可以爱干嘛干嘛。还有一个区别在于内存分配,new在堆中,另一在栈中,一般说两者一样,但如果你在对话框中开了很大的空间做成员,第二种就会有问题了,因为栈空间是受限的。但据说解决栈溢出用static申明,在全局数据区分配内存就可以了(我没试过)!模态消息不进message loop;非模态消息进入message loop。
如何使用vc的非模态对话框
1、非模态对话框的模板必须具有Visible风格,否则对话框将不可见,而模态对话框则无需设置该项风格。更保险的办法是调用CWnd::ShowWindow(SW_SHOW)来显示对话框,而不管对话框是否具有Visible风格。
2、非模态对话框对象是用new操作符在堆中动态创建的,而不是以成员变量的形式嵌入到别的对象中或以局部变量的形式构建在堆栈上。通常应在对话框的拥有者窗口类内声明一个指向对话框类的指针成员变量,通过该指针可访问对话框对象。
3、*通过调用CDialog::Create函数来启动对话框,而不是CDialog::DoModal,这是模态对话框的关键所在。由于Create函数不会启动新的消息循环,对话框与应用程序共用同一个消息循环,这样对话框就不会垄断用户的输入。Create在显示了对话框后就立即返回,而DoModal是在对话框被关闭后才返回的。众所周知,在MFC程序中,窗口对象的生存期应长于对应的窗口,也就是说,不能在未关闭屏幕上窗口的情况下先把对应的窗口对象删除掉。由于在Create返回后,不能确定对话框是否已关闭,这样也就无法确定对话框对象的生存期,因此只好在堆中构建对话框对象,而不能以局部变量的形式来构建之。
4、代码示例:
对话框资源ID为:IDD_MYDIALOG
对话框的类定义为:CMyDialog
CMyView的成员变量为:CMyDialog* m_myDlg
CMyView::OnOpenDialogButton()
{
CMainFrame* pWnd=(CMainFrame*)AfxGetMainWnd();
ASSERT_VALID(pWnd); //定义父窗口指针pWnd
m_myDlg=new CMyDialog(pWnd); //堆分配非模态对话框内存空间, //CMyDialog(CWnd* pParent=NULL)为构造函数
m_myDlg->Create(IDD_MYDIALOG,pWnd);
m_myDlg->ShowWindow(SW_SHOW);
//可用this指针代替pWnd指针,则省略头两行
}
5、必须有一个标志表明非模态对话框是否是打开的。这样做的原因是用户有可能在打开一个模态对话框的情况下,又一次选择打开命令。程序根据标志来决定是打开一个新的对话框,还是仅仅把原来打开的对话框激活。通常可以用拥有者窗口中的指向对话框对象的指针作为这种标志,当对话框关闭时,给该指针赋NULL值,以表明对话框对象已不存在了。
[注]:在C++编程中,判断一个位于堆中的对象是否存在的常用方法是判断指向该对象的指针是否为空。这种机制要求程序员将指向该对象的指针初始化为NULL值,在创建对象时将返回的地址赋给该指针,而在删除对象时将该指针置成NULL值。
6、必须调用CWnd::DestroyWindow而不是CDialog::EndDialog来关闭非模态对话框。调用CWnd::DestroyWindow是直接删除窗口的一般方法。由于缺省的CDialog::OnOK和CDialog::OnCancel函数均调用EndDialog,故程序员必须编写自己的OnOK和OnCancel函数并且在函数中调用DestroyWindow来关闭对话框。
7、因为是用new操作符构建非模态对话框对象,因此必须在对话框关闭后,用delete操作符删除对话框对象。在屏幕上一个窗口被删除后,框架会调用CWnd::PostNcDestroy,这是一个虚拟函数,程序可以在该函数中完成删除窗口对象的工作,具体代码如下
void CModelessDialog::PostNcDestroy
{
delete this; //删除对象本身
}
这样,在删除屏幕上的对话框后,对话框对象将被自动删除。拥有者对象就不必显式的调用delete来删除对话框对象了。也可以通过由的DestroyWindow()引起的WM_DESTROY消息处理函数OnDestroy()种实现delete this操作。
void CModelessDlg::OnDestroy()
{
CDialog::OnDestroy();
delete this;
}
模态对话框的使用
在下面的一篇文章里叙述的很详尽,所以就摘抄过来,呵呵
Visual C++模态对话框消息处理机制剖析
摘要:消息驱动机制是Windows操作系统的根本,模态对话框消息处理又是不同于一般消息处理的特殊形式。通过分析这种消息机制的原理,可用来处理类似的程序设计要求。
在Windows操作系统中,面向用户的GUI基本上可分为对话框形式和文档/视图两种表现形式。对话框的显示方式又可分为模态对话框和非模态对话框,以适应不同的用户交互需求。由于对话框和文档/视图框架结构各有特色,能不能将文档/视图框架结构当作一对话框来使用,或在对话框中实现文档/视图框架结构内的特色功能呢,答案是肯定的。
下面,从Windows 操作系统消息驱动机制开始,进而探讨模态对话框实现过程的消息封装、传递和处理机制,最后以模态的形式显示应用到文档/视图框架结构中的实例作为对所讲内容的验证和实践。
一、Windows消息机制
Windows是一种面向对象的体系结构,Windows环境和应用程序都是通过消息来交互的。Windows应用程序开始执行后,Windows为该程序创建一个"消息队列(message queue)",用以存放邮寄给该程序可能创建的各种不同窗口的消息。消息队列中消息的结构(MSG)为:
typedef struct tagMSG{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;
其中第一个成员变量是用来标识接收消息的窗口句柄;第二个参数便是消息标识号,如WM_PAINT;第三个和第四个参数的具体意义同message值有关,均为消息参数。前四个参数是非常重要和经常用到的,至于后两个参数则分别表示邮寄消息的时间和光标位置(屏幕坐标)。把消息传送到应用程序有两种方法:一种是由系统将消息"邮寄(post)"到应用程序的"消息队列"这是"进队消息"Win32 API有对应的函数: PostMessage(),此函数不等待该消息处理完就返回;而另一种则是由系统在直接调用窗口函数时将消息"发送(send)"给应用程序的窗口函数,属于"不进队消息"对应的函数是SendMessage()其必须等待该消息处理完后方可返回。
对于每一个正在执行的Windows应用程序,系统为其建立一个"消息队列",即应用程序队列,用来存放该程序可能创建的各种窗口的消息。应用程序中含有一段称作"消息循环"的代码,用来从消息队列中检索这些消息并把它们分发到相应的窗口函数中。
消息循环代码是应用程序中主函数winmain ( )中类似如下的程序段:
while(GetMessage(&&msg,NULL,NULL,NULL))
{ file://从消息队列中取得消息
TranslateMessage(&&msg);
file://检索并生成字符消息WM_CHAR
DispatchMessage(&&msg);
file://将消息发送给相应的窗口函数
}
由此可见,所谓"消息循环",实际是程序循环。
Windows 应用程序创建的每个窗口都在系统核心注册一个相应的窗口函数,窗口函数程序代码形式上是一个巨大的switch 语句,用以处理由消息循环发送到该窗口的消息,窗口函数由Windows 采用消息驱动的形式直接调用,而不是由应用程序显示调用的,窗口函数处理完消息后又将控制权返回给Windows。
二、模态对话框的消息处理
由上面我们看到,Windows是一个巨大的消息驱动结构,由用户发出消息,系统响应处理。非模态对话框是响应一个消息,系统处理一个消息,处理完毕后返回控制权给Windows。文档/视图框架结构与其类似。模态对话框在对话框创建后,挂起外部的消息,只是响应对话框内部的消息,而外部消息则全部"过滤"掉了,直到系统接收到WM_DESTROY或WM_CLOSE后,系统返回控制权给模态对话框创建前的线程,继续模态对话框创建前的线程将执行下面的代码。
让我们看看下面的对话框DoModal实现代码:
{
… …
// Disable 父窗口 (在创建对话框前)
HWND hWndParent = PreModal();
AfxUnhookWindowCreate();
BOOL bEnableParent = FALSE;
if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
{
::EnableWindow(hWndParent, FALSE);
bEnableParent = TRUE;
}
TRY
{
// 创建模态对话框
AfxHookWindowCreate(this);
if (CreateDlgIndirect(lpDialogTemplate,CWnd::FromHandle(hWndParent), hInst))
{
if (m_nFlags & WF_CONTINUEMODAL)
{
// 进入模式循环
DWORD dwFlags = MLF_SHOWONIDLE;
if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG;
VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
}
}
}
CATCH_ALL(e)
{
DELETE_EXCEPTION(e);
m_nModalResult = -1;
}
END_CATCH_ALL
file:///C|/Documents%20and%20Settings/chenxintao/%E3%80%8C%E5%BC%80%E5%A7%8B%E3%80%8D%E8%8F%9C%E5%8D%95/%E7%A8%8B%E5%BA%8F/Zoundry%20Raven/Enable 父窗口
if (bEnableParent)
::EnableWindow(hWndParent, TRUE);
if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
::SetActiveWindow(hWndParent);
// 删除对话框
DestroyWindow();
PostModal();
… …
}
可以看到,在此实现代码中,并没有开辟新的线程。系统是在RunModalLoop()中进行消息循环。当 m_nFlags 为 WF_CONTINUEMODAL时,系统继续模式状态。RunModalLoop()函数实际上也是一for(;;)循环,控制重新分派Windows消息。直到ContinueModal()返回FALSE,而当调用EndModalLoop()时,ContinueModal()返回FALSE。此时,标志着模态显示的结束。因此,实现模态对话框消息处理的核心部分为RunModalLoop()和EndModalLoop()函数。
三、以模态的形式显示应用到文档/视图框架结构实例
(1)新建一工程文件:ModeFrame,选取MFC AppWizard(exe)。
(2)第二步选取Single document(单文档)。
(3)其余几步均为缺省值。
(4)用ClassWizard添加一新类CSubModeFrame,以CFrameWnd为基类。
(5)添加CsubModeFrame的实现函数DoMode();
int CsubModeFrame::DoModal()
{
HWND hWndParent = m_hWndPrt;
CRect rc(0,0,400,400);
CWnd *pParent = CWnd::FromHandle(hWndParent);
DWORD dwStyle=WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_THICKFRAME | WS_VISIBLE | WS_SYSMENU | WS_CAPTION;
if(!Create(NULL,"模态文档/试图框架 ",dwStyle,rc,pParent,NULL)) return FALSE;
BOOL bEnableParent = FALSE;
if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
{
::EnableWindow(hWndParent,FALSE);
::EnableWindow(m_hWnd,TRUE);
bEnableParent = TRUE;
}
CenterWindow();
TRY
{
// enter modal loop
DWORD dwFlags = MLF_SHOWONIDLE;
if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG;
VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
}
CATCH_ALL(e)
{
DELETE_EXCEPTION(e);
m_nModalResult = -1;
}
END_CATCH_ALL
if (bEnableParent)
::EnableWindow(hWndParent, TRUE);
if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
::SetActiveWindow(hWndParent);
// destroy modal window
DestroyWindow();
return m_nModalResult;
}
(6)添加CsubModeFrame的实现函数EndMode()
void CSubFrame::EndModal(){
ASSERT(::IsWindow(m_hWnd));
if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL)) {
EndModalLoop(1);
}
}
(7)添加CModeFrameView的实现函数OnLButtonDblClk()
在此函数的消息处理中:可以象显示对话框一样处理CsubModeFrame类。
CSubModeFrame SubModeFrame;
If(SubModeFrame.DoMode()){ MessageBox("Mode Ok");}
(8)编译运行工程,双击视图,就会弹出模态的子文档/视图框架结构
结论: 通过上面的分析和实例可以看出,深入研究了解Windows的消息处理机制,可利用消息对Windows的事件进行任意的定制和处理,不用拘泥于系统原有的模式。对进行深入Windows编程是很有必要的。
利用WH_CBT Hook将非模态对话框显示为模态对话框
http://blog.youkuaiyun.com/itclock/archive/2009/03/26/4027173.aspx
1、意图
有时候我们希望将非模态窗口显示为模态窗口。比如在IE的"文件"菜单下选择"打印",弹出的"打印"对话框就是非模态的(也许我们不太清楚Microsoft的设计意图,一般来说这里的"打印"对话框应该是模态的)。这种情况下如何将"打印"对话框显示为模态的呢(这个对话框对我们来说是Black Box)?
2、简单实现
简单地说,模态窗口显示时,其父窗口是被Disable的,所以模态窗口才呈现"模态",所以只要在显示我们非模态窗口前将父窗口Disable即可实现,如下:
……
AfxGetMainWnd()->EnableWindow(FALSE);//将主窗口Disable,显示出的非模态窗口就变成模态的了
ShowModelessWindow();
……
问题在于非模态窗口显示之后是立即返回的,那我们将父窗口Enable的代码放在哪里呢?笨办法是用时钟,不断地检测显示出来的非模态窗口是否已经关闭,若关闭则将父窗口Enable。
当然,还要更好的办法。
3、WH_CBT Hook
WH_CBT钩子的详细说明请参阅MSDN,我们仅仅需要知道的是在窗口创建、销毁之前系统都会调用挂上了WH_CBT的钩子函数,这正是我们需要的。具体就是在显示非模态窗口之前挂上我们的WH_CBT钩子处理函数,之后非模态窗口创建的句柄就可以在钩子函数的nCode为HCBT_CREATEWND(创建窗口)时从wParam参数获得,将其保存下来,并在钩子函数的nCode为HCBT_DESTROYWND(销毁窗口)时与wParam参数进行比较,如果匹配则恢复主窗口的Enable状态。
2、实现
1)首先定义两个变量,此处为全局静态变量。
static HHOOK g_hHook = NULL;
static HWND g_hWndDialog = NULL;//用以保存窗口句柄
2)再添加一个函数CbtProc,由于是回调函数,注意要声明为static。
static LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam);
3)挂钩
假设下面是我们的某个浏览器中调用"打印"对话框的函数
void CMyHtmlView::OnFilePrint()
{
AfxGetMainWnd()->EnableWindow(FALSE);
g_hWndDialog = 0; //可能多次调用,需要重置保存窗口句柄的变量
g_hHook = SetWindowsHookEx(WH_CBT, CbtProc, NULL, GetCurrentThreadId());
if (!g_hHook)
{
AfxGetMainWnd()->EnableWindow(TRUE);
return;
}
调用"打印"对话框
}
LRESULT CALLBACK CMyHtmlView::CbtProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch (nCode)
{
case HCBT_CREATEWND:
{
HWND hWnd = (HWND)wParam;
LPCBT_CREATEWND pcbt = (LPCBT_CREATEWND)lParam;
LPCREATESTRUCT pcs = pcbt->lpcs;
3)挂钩
假设下面是我们的某个浏览器中调用"打印"对话框的函数
void CMyHtmlView::OnFilePrint()
{
AfxGetMainWnd()->EnableWindow(FALSE);
g_hWndDialog = 0; //可能多次调用,需要重置保存窗口句柄的变量
g_hHook = SetWindowsHookEx(WH_CBT, CbtProc, NULL, GetCurrentThreadId());
if (!g_hHook)
{
AfxGetMainWnd()->EnableWindow(TRUE);
return;
}
调用"打印"对话框
}
LRESULT CALLBACK CMyHtmlView::CbtProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch (nCode)
{
case HCBT_CREATEWND:
{
HWND hWnd = (HWND)wParam;
LPCBT_CREATEWND pcbt = (LPCBT_CREATEWND)lParam;
LPCREATESTRUCT pcs = pcbt->lpcs;
if ((DWORD)pcs->lpszClass == 0x00008002)//#32770,"打印"对话框类名
{
if ( g_hWndDialog == 0 )
g_hWndDialog = hWnd; // 只保存一次保存"打印"窗口的句柄
}
break;
}
case HCBT_DESTROYWND:
{
HWND hwnd = (HWND)wParam;
if (hwnd == g_hWndDialog)
{
AfxGetMainWnd()->EnableWindow(TRUE);//恢复窗口状态
UnhookWindowsHookEx(g_hHook);//去除挂钩
}
break;
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
很简单吧,更重要的是这种方法确实有效。
Powered by Zoundry Raven