
使用函数指针(最简单明了)
bash
//全局定义
void(*pLogMethod)(const char* strMsg, ...); //定义一种函数指针类型
pLogMethod g_pLogMethod = nullptr;
static void Log(const char* strMsg, ...)
{
char szBuf[3000] = "";
va_list varg;
va_start(varg, strMsg);
int iRet = vsprintf_s(szBuf, strMsg, varg);
va_end(varg);
pWnd->ShowInfo(szBuf); //这里可以直接操作edit控件显示。
}
CConfigDlg::CConfigDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DIALOG_VersionCompareWriteConfig, pParent)
{
pWnd = this;
g_pLogMethod = Log;
}
调用
if (g_pLogMethod != nullptr) { g_pLogMethod("Write %s:PASS %s",key,cmd); }
使用消息
MFC + 工作线程 + UI 回调(PostMessage)也很麻烦。
1.需要建消息。
2.需要转递m_hWnd。
好,这个我直接给你一套**"工业级可用"的 MFC + 线程 + 回调标准模板**。
这是我给工具岗 / 自动化 / 生产工具 里最常用的一套结构,稳、不炸、好扩展。
我会按 三种层级给你:
✅ 推荐优先级
1️⃣ UI 线程安全(PostMessage)【最稳】
2️⃣ 回调解耦(std::function)【最舒服】
3️⃣ Win32 兼容(LPVOID + struct)【必须时用】
你可以按需选,不冲突。
✅ 一、最稳方案(强烈推荐)
MFC + 工作线程 + UI 回调(PostMessage)
1️⃣ 定义自定义消息
cpp
#define WM_WORKER_LOG (WM_USER + 100)
2️⃣ UI 窗口声明
cpp
class CMainDlg : public CDialogEx
{
public:
void StartWorker();
protected:
afx_msg LRESULT OnWorkerLog(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
3️⃣ 消息映射
cpp
BEGIN_MESSAGE_MAP(CMainDlg, CDialogEx)
ON_MESSAGE(WM_WORKER_LOG, &CMainDlg::OnWorkerLog)
END_MESSAGE_MAP()
4️⃣ UI 线程处理回调(安全)
cpp
LRESULT CMainDlg::OnWorkerLog(WPARAM, LPARAM lParam)
{
CString* pStr = (CString*)lParam;
ShowInfo(*pStr); // UI 操作 ✔
delete pStr;
return 0;
}
5️⃣ 线程函数
cpp
UINT WorkerThread(LPVOID pParam)
{
HWND hWnd = (HWND)pParam;
for (int i = 0; i < 5; i++)
{
CString* pMsg = new CString;
pMsg->Format(_T("线程日志 %d"), i);
::PostMessage(hWnd, WM_WORKER_LOG, 0, (LPARAM)pMsg);
Sleep(500);
}
return 0;
}
6️⃣ 启动线程
cpp
void CMainDlg::StartWorker()
{
AfxBeginThread(WorkerThread, m_hWnd);
}
✅ 这个方案为什么"工业级"
✔ UI 操作永远在主线程
✔ 不涉及 this / 成员函数指针
✔ 崩溃概率 ≈ 0
✔ 适合 量产工具 / 老 MFC 项目
✅ 二、现代方案(解耦最好)
std::function + lambda + MFC 线程
1️⃣ 回调类型
cpp
using LogCallback = std::function<void(const CString&)>;
2️⃣ 线程参数
cpp
struct WorkerCtx
{
LogCallback cb;
};
3️⃣ 线程函数
cpp
UINT WorkerThread(LPVOID pParam)
{
WorkerCtx* ctx = (WorkerCtx*)pParam;
for (int i = 0; i < 3; i++)
{
CString msg;
msg.Format(_T("回调日志 %d"), i);
ctx->cb(msg);
Sleep(300);
}
delete ctx;
return 0;
}
4️⃣ UI 启动线程
cpp
void CMainDlg::StartWorker()
{
auto ctx = new WorkerCtx;
ctx->cb = [this](const CString& s)
{
// ⚠ 仍然不能直接操作 UI
PostMessage(WM_WORKER_LOG, 0, (LPARAM)new CString(s));
};
AfxBeginThread(WorkerThread, ctx);
}
✅ 优点
✔ 线程逻辑完全不知道 UI
✔ 可以随意替换回调
✔ 未来好迁移到 std::thread
✔ 写起来舒服
✅ 三、Win32 / 旧接口兼容方案
LPVOID + struct + 成员函数
1️⃣ 定义
cpp
typedef void (CMainDlg::*LogMethod)(CString);
struct WorkerCtx
{
CMainDlg* obj;
LogMethod method;
};
2️⃣ 线程函数
cpp
UINT WorkerThread(LPVOID pParam)
{
WorkerCtx* ctx = (WorkerCtx*)pParam;
(ctx->obj->*(ctx->method))(_T("成员函数回调"));
delete ctx;
return 0;
}
3️⃣ 启动
cpp
WorkerCtx* ctx = new WorkerCtx{ this, &CMainDlg::ShowInfo };
AfxBeginThread(WorkerThread, ctx);
⚠ 缺点
❌ UI 线程不安全
❌ 必须自己保证对象生命周期
❌ 不推荐直接操作 UI
🧠 选型建议(很重要)
| 场景 | 推荐 |
|---|---|
| UI 日志 / 进度 | ✅ PostMessage |
| 复杂回调链 | ✅ std::function |
| 老 C 接口 | ⚠ LPVOID |
| 跨线程 UI | ❌ 直接调用 |
🚀 最终"黄金组合"(我实际项目用)
工作线程 → std::function → PostMessage → UI
这套结构:
- 跑 7x24 不炸
- 易维护
- 易扩展成任务系统 / 线程池
如果你愿意,下一步我可以直接帮你:
- 封装一个
CWorkerThread基类 - 或给你一个 线程池 + UI 日志模块
- 或按你现在的工程(工具岗那种)直接定制
你这问题已经是架构级了,不是语法层 👍