概述
基于MFC的在线程里创建模态/非模态对话框(使用工作线程和界面线程)
工作线程
首先是创建模态的对话框:
要清楚一点:DoModal函数是阻塞的,程序执行到这里就停了,直到把该模态对话框关闭之后,才继续执行。而非模态对话框是不管对话框创建完消没消失,程序都会向下走。
UINT _cdecl C多线程Dlg::ThreadProc(LPVOID lpParameter)
{
C多线程Dlg *pThisDlg = (C多线程Dlg *)lpParameter;//获取当前主对话框的指针,要进行强转
CTestDlg dlg1;
dlg1.DoModal();//在线程函数中创建模态的对话框,把dlg对象显示为模态对话框
return 0;
}
void C多线程Dlg::OnBnClickedBtn()
{
CWinThread *pThread = AfxBeginThread(ThreadProc,this);
}
线程退出了,线程函数就结束了,响应申请的资源就释放了,所以DoModal阻塞在那儿模态对话框不会退出,非模态对话框就自然而然的消失了。
工作线程没有消息循环,对话框界面的刷新工作,包括接受用户的鼠标键盘操作,都不会响应,但我们可以给他一个消息循环,但如果都这样做了,那为什么不去用界面线程呢,界面线程考虑的会更全面。
创建非模态对话框的代码如下:
UINT _cdecl C多线程Dlg::ThreadProc(LPVOID lpParameter)
{
C多线程Dlg *pThisDlg = (C多线程Dlg *)lpParameter;//获取当前主对话框的指针,要进行强转
CTestDlg *pTestDlg = new CTestDlg(); //创建指针非模态对话框才可以正常显示,开辟一个存放CTestDlg类型的存储空间,返回一个指向该存储空间的地址(即指针) 可以多处使用,和直接创建相比需要手动销毁,
//pTestDlg->Create(IDD_DIALOG1,pThisDlg);//参数Ⅰ创建的非模态对话框模板的ID 参数Ⅱ父窗口指针 但这个语句是不对的 两个对话框的资源没有在一个线程中,可以设为NULL,以桌面为其父窗口
pTestDlg->Create(IDD_DIALOG1,NULL);
pTestDlg->ShowWindow(SW_SHOW);//显示对话框
//以下几条语句(消息循环):从当前线程的消息队列中做消息处理
MSG msg = {0};
while(GetMessage(&msg, NULL, 0, 0))//无限的取消息
{
TranslateMessage(&msg);//消息的转换
DispatchMessage(&msg);//消息的分发
}
不加消息循环语句,对话框弹出几毫秒就随着线程结束消失了。
如何创建界面线程?
第一步:在解决方案中右击项目,选择添加,选择类,之后按下图选择,点击添加,
如此我们就向我们的工程中插入了一个CWinThread的子类。
第二步:重载InitInstance函数,在InitInstance函数(界面线程的初始化函数)中进行界面的创建。
先在UIThreadApp.cpp中添加头文件#include “TestDlg.h”,之后在函数中添加代码
BOOL CUIThreadApp::InitInstance()
{
// TODO: 在此执行任意逐线程初始化
CTestDlg dlg;
dlg.DoModal();
return TRUE;
}
第三步:调用AfxBeginThread函数开启界面线程。
在主对话框.cpp文件中添加头文件#include “UIThreadApp.h”,
首先创建模态对话框:
点击响应函数:
void C多线程Dlg::OnBnClickedBtn()
{
//m_Num = 123;
//CWinThread *pThread = AfxBeginThread(ThreadProc,this);
AfxBeginThread(RUNTIME_CLASS(CUIThreadApp));//参数 CWinThread类的子类,该函数会实例化该类的对象并让界面线程运行起来
}
界面线程初始化函数:
BOOL CUIThreadApp::InitInstance()
{
// TODO: 在此执行任意逐线程初始化
CTestDlg dlg;
dlg.DoModal();
return FALSE;//把TURE改为False 我们把模态框退出之后,界面线程就没有其存在的意义,我们就让它退出。设为TURE则会导致无法执行界面线程退出函数造成内存泄漏
}
创建非模态对话框
点击响应函数:
void C多线程Dlg::OnBnClickedBtn()
{
AfxBeginThread(RUNTIME_CLASS(CUIThreadApp));//参数 CWinThread类的子类,该函数会实例化该类的对象并让界面线程运行起来
}
界面线程初始化函数:
BOOL CUIThreadApp::InitInstance()
{
// TODO: 在此执行任意逐线程初始化
CTestDlg *pTestDlg = new CTestDlg();
pTestDlg->Create(IDD_DIALOG1,NULL);
pTestDlg->ShowWindow(SW_SHOW);//显示对话框
pTestDlg->RunModalLoop();//开启非模态框消息循环,没有就会一闪而过
return FALSE;
}
/*******************************************************************************************
MFC在子线程中创建窗口(PostMessage方法)
1、创建子线程
C++创建线程的方式比较多
1)最简单易用的<thread>头文件,但是这种方法创建的子线程中无法给主线程PostMessage消息(也可能是我操作有误,总之没成功)
2)3)4)参见VC创建线程的三种方法
第3、4种用在MFC程序中貌似也不行,多次尝试之下我用了AfxBeginThread()方法成功了
void CMFCDLLTestDlg::OnBnClickedMessage() { // TODO: 在此添加控件通知处理程序代码 // 启动websocket线程 AfxBeginThread((AFX_THREADPROC)MsgThread, (VOID*)this, THREAD_PRIORITY_NORMAL, 0, 0, NULL); }
我这里是在一个按钮点击事件中启动了一个websocket线程,全局线程函数MsgThread()
2、通过自定义消息创建窗口
在MFC程序中,在子线程中直接调用Create()方法无法创建非模态窗口,貌似子线程的循环阻塞了创建过程,所以需要用自定义消息方法通知主线程来创建
2.1 自定义消息
MFC自定义消息其实不难,分三个步骤
1、定义一个消息ID
我的程序名叫MFCDLLTestDlg,所以在MFCDLLTestDlg.cpp中定义下一个消息ID
#define TEST_SENDMSG WM_USER+200//给消息一个ID
2、定义消息处理函数
消息处理函数是用来处理收到的自定义消息的,这个有两种方法,可以通过类向导添加一个自定义消息处理,或者自己手写也行,使用类向导可以直接绑定,省了第三步
类向导方式:切换到类试图--->类向导--->消息--->添加自定义消息,然后输入自定义的消息ID和处理函数名称就好了
手写方式和类向导一样,反正消息ID必须是自己定义的,固定WM_USER+一个数,不重复就行
然后再头文件中声明消息处理函数,注意在对话框主类中写
afx_msg LRESULT OnTestSendmsg(WPARAM wParam, LPARAM lParam);
然后在cpp中定义
LRESULT CMFCDLLTestDlg::OnTestSendmsg(WPARAM wParam, LPARAM lParam) { switch (wParam) { case TEST_SENDMSG: CMsgWindow * p_MsgWindow = new CMsgWindow(); p_MsgWindow->SetSkin(MAKEINTRESOURCE(IDB_BITMAP1)); //p_MsgWindow->SetSkin(MAKEINTRESOURCE(IDB_BITMAP2)); //p_MsgWindow->SetSkin(MAKEINTRESOURCE(IDB_BITMAP3)); CString *cmsg = (CString *)lParam; if (!p_MsgWindow->Create(m_hWnd, _T("通知"))) { AfxMessageBox(L"Create Failed!"); return -1; } p_MsgWindow->SetMsg(L"高仿QQ新闻右下角弹窗", *cmsg, L"http://blog.csdn.net/jackystudio"); p_MsgWindow->Show(); break; } return LRESULT(); }
这里我是在收到消息后弹了个窗,有兴趣的访问一下这个 偶尔e网事的博客_CSDN博客-cocos2d-x,玩转cocos2d-x,零基础学Git领域博主 博客,我从这里找的漂亮的弹窗程序
注意这个函数的两个参数[ WPARAM wParam, LPARAM lParam ],这个是可以自己类型转换的,常用的可能就是这种,第一个参数为消息类型,第二个参数为字符串,整数等其他参数,这里是个Cstring字符串
这两个参数是在PostMessage函数中传进来的,下面会看到
3、添加消息处理映射
有了消息ID和处理函数,还要把两者关联起来,这就是消息映射同样是在主类中操作,找到MESSAGE_MAP
BEGIN_MESSAGE_MAP(CMFCDLLTestDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_WIN_TEXT, &CMFCDLLTestDlg::OnBnClickedWinText) ON_BN_CLICKED(IDC_NEW_DLG, &CMFCDLLTestDlg::OnBnClickedNewDlg) ON_BN_CLICKED(IDC_MESSAGE, &CMFCDLLTestDlg::OnBnClickedMessage) ON_MESSAGE(TEST_SENDMSG, &CMFCDLLTestDlg::OnTestSendmsg) END_MESSAGE_MAP()
可以看到系统消息和按钮点击事件的映射都是在这里的,需要注意的是看清楚是主类的消息映射 BEGIN_MESSAGE_MAP(CMFCDLLTestDlg, CDialogEx) ,我就第一次写在了About类的里面
2.2 发送消息
在子线程函数中用PostMessage发送消息,一般用这个,SendMessage也行,一个同步一个异步
// 省略线程函数其他逻辑 // ... // 发送消息 ::PostMessage(AfxGetMainWnd()->GetSafeHwnd(), TEST_SENDMSG, (WPARAM)TEST_SENDMSG, (LPARAM)cmsg);
这里需要注意PostMessage函数加了作用域限定符,否则这个函数有好几个,调用方法不同
第一个参数是主窗口句柄,第二个参数是消息ID,第三、四个参数对应上面消息处理函数的两个参数
然后当发送消息函数被执行的时候,窗口主线程就会收到消息,执行创建窗口函数
当然只要把消息ID和参数一换,比如说换成某个按钮的点击事件ID或者系统消息ID,就可以做些其他事情了
/**********************************************************
启动线程的时候有个参数LPVOID ,可以通过此参数把主线程的中的对象指针传递进去,在子线程中用这个指针来调用它的成员函数。
但要注意的是,不要在子线程中直接用指针调用主线程中的窗口对象的成员函数。这样会引发一些潜在错误。因为MFC不是线程安全的。
最后
以上就是机智自行车为你收集整理的MFC中在子线程中创建UI控件及对话框的方法1、创建子线程2、通过自定义消息创建窗口的全部内容,希望文章能够帮你解决MFC中在子线程中创建UI控件及对话框的方法1、创建子线程2、通过自定义消息创建窗口所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复