MFC中在子线程中创建UI控件及对话框的方法

2023-05-16

基于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不是线程安全的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MFC中在子线程中创建UI控件及对话框的方法 的相关文章

  • dll 中的 MFC LoadString 失败

    我在 dll 中有一个静态函数 它使用 LoadString 从资源加载字符串 当我从该 dll 调用此函数时 一切正常 但是 当我从其他模块 activeX 控件 调用此函数时 LoadString 失败并出现错误 ERROR RESOU
  • 托管和非托管 C++/MFC 可以混合在一个 dll 中吗?

    以前 我们有 MFC VC6 VB6 和 C 应用程序中的软件 需要调用用 C 和 MFC 编写的相同引擎 该引擎需要 C 来提高速度 当时我们决定使用 COM 作为接口 因为所有三个都可以使用它 并且在编组等方面的问题最少 我们的 MFC
  • Boost::序列化和 MFC Doc/View 架构

    我正在移植现有的 MFC C 应用程序以对 XML 文件使用 Boost Serialization 我的 CDocument 对象包含应用程序的所有数据 我已将序列化函数实现为 template
  • CloseWindow和WM_CLOSE有什么关系

    我现在有点困惑 是吗 WM CLOSE http msdn microsoft com en us library windows desktop ms632617 28v vs 85 29 aspx and CloseWindow htt
  • AfxGetAppName() 返回垃圾字符

    我的应用程序中有以下代码行 CString strAppName AfxGetAppName 有时会充满strAppName出现了垃圾字符 我不明白为什么 有人有主意吗 TIA 如果你改变的话这是可能的m pszAppName手动 在应用程
  • 与 UltraHD 兼容的 CHtmlView

    CHtmlView与 UltraHD 分辨率不兼容 实现 UltraHD 感知并不仅仅在于使用正确的 HTML CSS 打印预览机制失败并裁剪页面 许多个月前 微软承认这是一个问题 但没有解决它 我的应用程序大量使用CHtmlView用于显
  • 如何将基数词转换为序数词

    有没有一种简单的方法可以将数字 1 2 3 转换为 1st 2nd 3rd 并且以这种方式我可以为该函数提供一种语言并具有它会返回我目标语言的正确形式吗 标准 C stl 或 boost 都可以 MFC 或 ATL win32 api 或我
  • 您可以将 CMFCVisualManager 与基于对话框的应用程序一起使用吗?

    您可以将 CMFCVisualManager 与基于对话框的应用程序一起使用来更改应用程序的外观吗 如果是这样 它是如何完成的 这个想法是使用随 MSVC 2008 发布的 MFC 功能包来更改控件 例如按钮 的形状 颜色等 不 不能这样做
  • 实现批量记录获取

    在程序开始时 我需要将数据从 MS Access 数据库 mdb 读取到下拉控件中 这样做是为了每当用户在该控件中键入内容时 应用程序都可以自动完成 不管怎样 从数据库中读取数据花了很长时间 所以我想我应该实现批量行获取 这是我的代码 CS
  • .NET(或 MFC)的高速图形控件?

    我需要编写一个数字示波器类型的应用程序 有很多很棒的静态绘图控件 但我需要一些可以绘制每秒处理 4000 个样本的 16 条轨迹的东西 有人知道 NET 的高速图形控件吗 我什至会选择 MFC 因为它可以封装到 NET 控件中 谢谢您的帮助
  • 错误 C2248: 'CObject::CObject' : 无法访问类 'CObject' afxwin.h 中声明的私有成员

    我试图让班级负责在灰色背景上放置一些文本 Score h pragma once class Score public Score Score void UpdateScore int points void UpdateLives int
  • 屏幕截图忽略了一些窗口

    我正在 MFC 中工作 我正在尝试捕获桌面的 bmp 我正在使用 GetDC NULL 来执行此操作 但它似乎忽略了特殊的皮肤窗口 它似乎忽略了用 UpdateLayeredWindow 绘制的窗口 此行为似乎仅发生在 Vista x64
  • 从模态 MFC 表单获取输入信息

    我已经创建了表格CPreparationDlg具有Edit Control 然后我创建了创建模态表单的应用程序 在按 确定 后 我需要将编辑控件中输入的文本读入主程序的变量中 最好的方法是什么 class CPreparationApp p
  • 具有键唯一性和按位置排序的 MFC 字典集合

    看着表上http msdn microsoft com en us library y1z022s1 28v vs 80 29 aspx core collection shape features http msdn microsoft
  • 如何将十六进制字符串转换为无符号长整型?

    我有以下十六进制值 CString str str T FFF000 如何将其转换为unsigned long 您可以使用strtol作用于常规 C 字符串的函数 它使用指定的基数将字符串转换为 long long l strtol str
  • 更改 GLUT 调用以与 MFC/C++ 一起使用

    我有一个使用 GLUT 进行 OpenGL 渲染的程序 现在我需要它位于 MFC 项目内部 以便它可以与另一个程序组件一起使用 我已经按照这个教程进行操作 http www codeguru com cpp g m opengl openf
  • Windows API 中逻辑坐标和设备坐标之间的混淆

    我一直在研究一个使用两个函数的 Visual Studio C Windows 应用程序项目SetWindowExt and SetViewportExt 我对这两个函数的作用以及为什么它们是必要的感到困惑 搜索这些函数 我得出了逻辑坐标和
  • 如何中止使用 wininet 发送的请求?

    我有一个 MFC 应用程序 用于向相应的服务器发送 post 请求 这是一个http请求 用于上传文件 但有要求中止发送的请求 就像用户正在发送一个大文件一样 用户应该能够在请求完成之前取消请求 我正在使用 wininet api Http
  • 通过 MFC 对话框中的代码更改 Tab 顺序

    我在 OnInitDialog 中通过代码创建了一个控件 但我找不到任何方法通过代码更改对话框的选项卡顺序 有人知道如何做到这一点吗 First Option use ctrl d on resource view in visual st
  • 调试器忽略动态加载的 DLL 中的错误

    我有一个与自编码 DLL 的调试相关的非常奇怪的问题 我有一个 MFC 驱动的基于对话框的应用程序 几个静态链接的项目和几个在运行时加载的 DLL 项目 我在调试中构建解决方案 运行应用程序 然后我可以轻松调试这些 DLL 项目 现在问题来

随机推荐