MFC应用程序,工作线程学习记录
如何在一个MFC应用程序中添加控制台输出(printf、cout)?
目标:在MFC应用程序中也可以使用cout,printf进行Debug
在程序初始化时(InitInstance中,位于“工程名.cpp”中)调用
AllocConsole(); // 分配控制台
freopen("CONOUT$", "w", stdout); // 将stdout重定向到控制台参数意义:
第一个参数 CONOUT$ : Windows 系统中的 “控制台输出设备”(Console Output)
是一个特殊的设备名(而非普通文件路径);
第二个参数 w :文件打开模式(Write,写入模式,还有其他模式)
第三个参数 stdout : 要重定向的标准流(Standard Output,标准输出)freopen函数意义:
核心作用是重新关联标准流(如 stdin/stdout/stderr)与指定的文件或设备
简单说就是 “重定向输入 / 输出的目标”
示例:freopen("log.txt", "w", stdout);(后续 printf 内容会写入 log.txt)。从属于C 标准库函数(头文件 <cstdio> 或 <stdio.h>)
这样操作以后,在启动MFC程序之后,会额外启动一个控制台窗口用来查看输出
如何简单实现多线程?(AfxBeginThread函数实现)
目标:使用多线程完成简单任务,并输出到控制台上
首先我们需要一个用于多线程执行的任务函数
UINT MyWorkerThread(LPVOID pParam)
{for (int i = 0; i < 100; i+=25) {printf("工作线程运行中%d%%\r\n", i);Sleep(1000);}printf("工作线程执行完毕!\n");return 0;
}
这里需要注意:
用 MFC 的 AfxBeginThread 或 Windows API 的 CreateThread:必须符合 UINT/DWORD 返回值 + LPVOID 参数的原型。该函数的“格式”是十分严格的,必须写成“返回值为UINT、参数为LPVOID” 这样的格式
但是,如果:使用 C++ 标准库 std::thread:无强制原型限制,函数可以灵活定义。
或者:直接同步调用(非线程):无任何限制,但也不是多线程。
其次我们要设定启动该线程的方式方法,比如说启动应用程序即刻启动,还是点击某个按键后触发(这里我使用的是点击一个按钮触发该线程)
如何添加按钮这里不多赘述:
void CTest24ThreadDlg::OnButtonStart()
{printf("线程即将启动\r\n");AfxBeginThread(MyWorkerThread,NULL); //创建并启动多线程printf("线程结束了吗?\r\n");
}
最后观察运行结果,并进行测试。执行完,结果应该是:
线程即将启动
线程结束了吗?
工作线程运行中:0%
工作线程运行中:25%
工作线程运行中:50%
工作线程运行中:75%
工作线程执行完毕!
工作线程与主线程(UI线程)通信及其交互
目标:通过工作线程,消息模式,来使得主线程完成反馈,而非单纯控制台反馈。
模式简述:
发送命令,准备工作:主线程(UI线程)通过发送指令令工作线程工作(或创建工作线程等)
受到命令,开始工作:工作线程完成自己的工作(UI线程仍可独立运行,不会阻塞)
完成工作,进行反馈:工作线程反馈完工信息等,反馈给主线程,令其有相应的反馈(弹窗等)
这就是一个消息模式流程。后续将围绕这几步骤进行实践
规定消息:无论是主界面还是工作界面都需要约定好的消息进行通信
这里采用宏定义进行定义消息:
#define MESSAGE_A 8888注意:宏定义的数值不可随便定义,因为有些数值已经被系统占用了
WM_USER + 100 是推荐的起始值,确保唯一性(据说)
处理函数:
在主线程窗口类中,声明并实现消息处理函数,用于接收工作线程的消息并更新 UI
放在“工程名Dlg.h”位置,即主线程窗口类(c工程名Dlg 类)中Protected处afx_msg LRESULT UpdateUI(WPARAM wParam, LPARAM lParam);代码命名要求较为严苛:除函数名外几乎无法修改
afx_msg 关键字必须添加在函数声明前,用于告诉 MFC 消息映射机制:“这是一个消息处理函数”
返回值类型 LRESULT固定为 LRESULT(32 位整数类型),用于表示消息处理的结果
(大多数情况下返回 0 即可)
WPARAM wParam, LPARAM lParam,必须包含这两个参数:
WPARAM:传递短整数或指针(32/64 位系统中长度与平台一致)。
LPARAM:传递长整数或指针
(功能与 WPARAM 类似,历史上为区分长度设计。现在两者在 32/64 位系统中通常等价)。
这两个参数是 Windows 消息机制的标准设计,用于传递消息的附加数据(如进度值、指针等)。
在源文件(工程名Dlg.cpp)中绑定消息与处理函数(消息映射)
在源文件(工程名Dlg.cpp)中绑定消息与处理函数(消息映射)ON_MESSAGE(MESSAGE_A, &CMessageTestDlg::UpdateUI) ON_MESSAGE(消息ID, 消息处理函数指针)要求放在
BEGIN_MESSAGE_MAP(C工程名Dlg, CDialog) /(即你的主窗口类,其父类)//{{AFX_MSG_MAP(CMessageTestDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON1, MessageSend)//}}AFX_MSG_MAPON_MESSAGE(MESSAGE_A, &C工程名Dlg::UpdateUI)END_MESSAGE_MAP()
这里,也就是夹在begin和end中间
注意Begin后面的参数,要是自己主窗口那个类,其他的是其他窗口的
这串代码是一个消息映射宏,每一个界面都会有,只不过是有没有内容,内容一样不一样的区别
主线程消息处理函数:
位于工程名Dlg.cpp中,信息映射后面LRESULT CMessageTestDlg::UpdateUI(WPARAM wParam, LPARAM lParam)
{MessageBox(_T("工作线程任务已完成!"), _T("提示"), MB_OK);return 0;
}注意,定义同样严苛:LRESULT 类名::函数名(WPARAM wParam, LPARAM lParam);
发送消息:仍设计为点击按钮发送消息
未完待续
