自定义MessageBox问题

windrainlytlyt 2012-08-11 05:16:49
公司要求我做一个自定义的MessageBox,主要为了界面好看一些,基本功能和windows 的MessageBox差不多。我设计的思路是用一个对话框,在响应MessageBox时把这个对话框DoModal出来。
现在的问题是,在基于对话框的MFC程序,主对话框中开了一个子线程B,在子线程B中显示了一个对话框C(ShowWindow显示),对话框C中调用了MessageBox的话,显示出来的消息框是基于主对话框的模式对话框。即这时候主对话框不能进行操作,而对话框C却是可以操作的。
请问大虾们有没有知道怎么解决这种问题的,至少能够说明一下问题的原因也好。我对于windows的线程反面的了解比较少。
...全文
408 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
向立天 2013-03-13
  • 打赏
  • 举报
回复
您好 我是本版版主 此帖已多日无人关注 请您及时结帖 如您认为问题没有解决可按无满意结帖处理 另外本版设置了疑难问题汇总帖 并已在版面置顶 相关规定其帖子中有说明 您可以根据规定提交您帖子的链接 如您目前不想结帖只需回帖说明 我们会删除此结帖通知 见此回复三日内无回应 我们将强制结帖 相关规定详见界面界面版关于版主结帖工作的具体办法
windrainlytlyt 2012-08-12
  • 打赏
  • 举报
回复
按楼上介绍的方法的确可行,在获取了MessageBox对话框的句柄后进行子类化定制,可以显示出自己想要的对话框。
但用钩子钩HCBT_ACTIVATE,在MessageBox显示出来时会进来一次,在关掉MessageBox时主窗口也会变为active,也会进来一次,这时获取的句柄就不是MessageBox窗口句柄,再子类化就会出错。
我尝试过用全局变量保存MessageBox的句柄,在进入HCBT_ACTIVATE时进行判断。这样如果只显示一个MessageBox没问题,但如果先后有多个MessageBox显示出来就会出错。请问有什么好的办法吗?
我的部分代码如下

HHOOK m_hMsgBox = NULL;
CNewDlg m_dlgNew; //此类为实际显示出来的对话框
HWND m_hwndMsgBox = NULL; //保存MessageBox窗口句柄,在退出MessageBox时置为NULL
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HWND hwnd;
if(nCode < 0)
return CallNextHookEx(m_hMsgBox, nCode, wParam, lParam);
switch(nCode)
{
case HCBT_ACTIVATE:
{
hwnd = (HWND)wParam;
// 我们已经有了message box的句柄,在这里我们就可以定制message box了!
if(m_hwndMsgBox == NULL)
m_hwndMsgBox = hwnd;
else
return 0;
if(m_dlgNew.GetSafeHwnd())
m_dlgNew.Detach();
m_dlgNew.SubclassWindow(hwnd);
m_dlgNew.Init();
m_dlgNew.CreateButtons();
return 0;
}
break;
}
// Call the next hook, if there is one
return CallNextHookEx(m_hMsgBox, nCode, wParam, lParam);
}
titer1 2012-08-11
  • 打赏
  • 举报
回复
回退一步,你的需求是定制messagebox..看看 下面


大家都知道MessageBox()函数的用法:

int MessageBox(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);

虽然在参数uType中可以指定一些样式,但你在程序中对MessageBox的外观能定义的不多。原因是当调用MessageBox()函数后,它在内部有自己的消息循环(所有的模式对话框都有自己的消息循环),返回时MessageBox对话框窗口已经被Destroy,所以你没有办法得到MessageBox对话框的窗口句柄。但你可以根据自己的不同需求用下面的方法中去定制你的MessageBox:

如果你只是想用自己的icon去代替系统MessageBox提供的那几种有限的icon,用MessageBoxIndirect()函数就可以了:

int MessageBoxIndirect(
CONST LPMSGBOXPARAMS lpMsgBoxParams // message box parameters
);

typedef struct {
UINT cbSize;
HWND hwndOwner;
HINSTANCE hInstance;
LPCTSTR lpszText;
LPCTSTR lpszCaption;
DWORD dwStyle;
LPCTSTR lpszIcon;
DWORD_PTR dwContextHelpId;
MSGBOXCALLBACK lpfnMsgBoxCallback;
DWORD dwLanguageId;
} MSGBOXPARAMS, *PMSGBOXPARAMS;

看到MSGBOXPARAMS结构中的lpszIcon吧,在使用过程中你应当准备一个合适的MSGBOXPARAMS结构,如果你要用自己的icon,你一定要用MB_USERICON这个flag。

上面我们也讲到不能定制MessageBox对话框的原因是没有办法得到它的窗口句柄,但我们真的没有办法了吗?当然有办法,那就是使用HOOK(钩子)机制。在windows系统中有多种HOOK,但在这里我们只用到HK_CBT类型的钩子。HK_CBT钩子过程在WM_MOVE、WM_SIZE、WM_ACTIVE、WM_CREATE、WM_DESTROY时被系统调用,所以HK_CBT钩子可以在这里用。下面让我们看如何实现MessageBox的定制过程。

1,安装HK_CBT钩子;
2,调用MessageBox()函数;
3,移除HK_CBT钩子。

整个过程很简单吧?我将介绍第一步和第三步。

安装HK_CBT钩子:

HHOOK hMsgBoxHook = SetWindowsHookEx(

WH_CBT, // Type of hook
CBTProc, // Hook procedure (see below)
NULL, // Module handle. Must be NULL (see docs)
GetCurrentThreadId() // Only install for THIS thread!!!
);

重要的是SetWindowHookEx()函数的后边两个参数,用它可以区别安装是一个全局钩子还是一个线程钩子,在这里我们只要安装一个线程钩子。所以我们将Module handle设置为NULL,同时将thread ID设为本线程的ID。

在SetWindowHookEx()函数中有一个hook procedure,这是window调用的一个回调函数,在windows系统中有一个HOOK链,我们在编写hook procedure时要注意保证此链的完整,所以我们的hook procedure要调用CallNextHookEx()函数。下面就是我们的hook procedure:
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HWND hwnd;
if(nCode < 0)
return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam);
switch(nCode)
{
case HCBT_ACTIVATE:
// 现在wParam中就是message box的句柄
hwnd = (HWND)wParam;
// 我们已经有了message box的句柄,在这里我们就可以定制message box了!
return 0;
}
// Call the next hook, if there is one
return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam);
}

移除HK_CBT钩子:

只要调用UnhookWindowsHookEx()函数就可以了

好了,我们将在上面的三步写成一个函数,如下:

int MsgBoxEx(HWND hwnd, TCHAR *szText, TCHAR *szCaption, UINT uType)
{
int ret;
// Install a thread hook, so we can customize it
hMsgBoxHook = SetWindowsHookEx(
WH_CBT,
CBTProc,
NULL,
GetCurrentThreadId()
);
// Display a standard message box
ret = MessageBox(hwnd, szText, szCaption, uType);
// remove the window hook
UnhookWindowsHookEx(hMsgBoxHook);
return ret;

}

其实你也可以钩住WM_CREATE消息,不过那样处理要复杂一些。我记得在早期的windows platform SDK中有这样一个例子。

你可能说,定制一个MessageBox有什么用处,我想有下面的用途:

1,你可以用CreateWindowEx()给MessageBox添加一个check box控件,并子类化MessageBox来处理check box的消息

2,通过子类化改变messagebox、button或icon,以便和你整个程序的界面风格相一致

只要有了MessageBox对话框的句柄,你能做的很多,很多...

另外,如果你对模式对话框的机理很了解,你可以自己写出一个"MessageBox"来代替系统MessageBox用在你的程序中。你可以参考Jeffrey Richter的《Windows 95程式设计指南》,在书中给出了模式对话框的伪码。这本书的繁体电子版可以在候捷的个人网站上下载。这种方法也比较简单(添加一个消息循环,Enable/Disable Owner窗口),我就在示例代码中不实现了。由于我工作较忙,在写示例代码时没有多少注释和防错,请读者谅解。


来自:http://www.viewarticle.com/vc/DingZhiMessageBox/
傻X 2012-08-11
  • 打赏
  • 举报
回复
我怎么记得谁调用谁卡死呢,难道记错了?
windrainlytlyt 2012-08-11
  • 打赏
  • 举报
回复
这样好像不可以。
接上面,假如对话框C调用MessageBox方法,MessageBox方法里是把CMsgBoxDlg类型的变量m_dlgMsgBox DoModal出来。我在CMsgBoxDlg的OnInitDialog方法里设置了父窗口指针为对话框C的指针。运行程序的话,一调用MessageBox就卡死了。
疯狂-的-蜗牛 2012-08-11
  • 打赏
  • 举报
回复
自己动态设置自定义的MessageBox对话框的父窗口(在哪个窗口上调用该窗口就是其父窗口)
然后DoModal

15,979

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧