如何在基于Qt的UI DLL点击事件中调用VC++ DLL/EXE函数(qtwinmigrate)
实现Qt UI DLL按钮点击触发VC++ DLL函数调用(基于QtWinMigrate)
问题概述
你需要实现的核心需求是:基于QtWinMigrate的Qt UI DLL中,点击pushButton时调用VC++ DLL/EXE中的elementsExampleCreateShapeActive()函数。你的思路是通过回调函数实现跨DLL通信,这个方向是对的,但现有代码存在几个细节问题需要调整才能正常运行。
现有代码的核心问题
- VC++端DLL加载后立即释放:
attachLibrary()里调用FreeLibrary(hDLL)会直接卸载Qt DLL,后续Qt UI触发回调时会因为DLL已不在内存而崩溃。 - 回调函数签名不匹配:Qt端的
SetFunCallBack是两个参数(回调函数指针+WCharCP参数),但VC端定义的PFunCallBack只接受一个参数,会导致参数传递错误。 - Qt DLL入口函数错误:Qt DLL里的
main是控制台程序入口,在DLL环境下应该用QtWinMigrate的QWinWidget来嵌入UI,直接调用main无法正确初始化Qt环境。
修正后的完整实现
1. Qt UI DLL代码修正
#include <QtWidgets/QPushButton> #include <QtWinMigrate/QWinWidget> // 定义回调函数指针,确保调用约定一致(CALLBACK即__stdcall) typedef const wchar_t* WCharCP; typedef void(CALLBACK *FunCallBack)(WCharCP); // 全局存储回调函数指针和参数 FunCallBack g_OnEvent = NULL; WCharCP g_unparsed = nullptr; // 导出设置回调的函数,参数与VC端完全匹配 extern "C" __declspec(dllexport) void CALLBACK SetFunCallBack(FunCallBack fun, WCharCP var) { g_OnEvent = fun; g_unparsed = var; } // 导出创建Qt UI的函数,用QWinWidget嵌入到VC窗口 extern "C" __declspec(dllexport) QWinWidget* CALLBACK CreateQtUI(HWND parentHwnd) { // 初始化Qt环境(仅第一次调用时初始化) static bool qtInitialized = false; if (!qtInitialized) { int argc = 0; QApplication app(argc, nullptr); qtInitialized = true; } // 创建QtWinMigrate容器,绑定到VC父窗口 QWinWidget* winWidget = new QWinWidget(parentHwnd); winWidget->setWindowTitle(L"Qt Embedded UI"); // 创建按钮并绑定点击事件 QPushButton* pushButton = new QPushButton(L"触发VC++函数", winWidget); pushButton->setGeometry(50, 50, 200, 30); QObject::connect(pushButton, &QPushButton::clicked, []() { if (g_OnEvent && g_unparsed) { // 调用VC端的回调函数 g_OnEvent(g_unparsed); } }); winWidget->show(); return winWidget; }
2. VC++ DLL代码修正
#include <windows.h> #include <iostream> #include <tchar.h> // 匹配Qt端的类型定义 typedef const wchar_t* WCharCP; typedef void(CALLBACK *FunCallBack)(WCharCP); // 导出的Qt DLL函数类型 typedef QWinWidget* (CALLBACK *CreateQtUIFunc)(HWND); typedef void(CALLBACK *SetFunCallBackFunc)(FunCallBack, WCharCP); // 全局存储Qt DLL句柄和UI指针,避免提前释放 HMODULE g_hQtDll = nullptr; void* g_pQtUI = nullptr; // 回调函数:接收Qt端触发,调用目标函数 void CALLBACK handleEvent(WCharCP unparsed) { // 调用VC++端的目标业务函数 elementsExampleCreateShapeActive(unparsed); std::wcout << L"回调触发,参数:" << unparsed << std::endl; } // 附加Qt UI到VC窗口的函数 void attachLibrary(HWND parentHwnd) { const TCHAR* dllName = _T("qtdialog.dll"); g_hQtDll = LoadLibrary(dllName); if (g_hQtDll == NULL) { std::cout << "无法加载DLL: " << dllName << std::endl; return; } // 获取创建Qt UI的函数 CreateQtUIFunc createQtUI = (CreateQtUIFunc)GetProcAddress(g_hQtDll, "CreateQtUI"); if (createQtUI == nullptr) { std::cout << "找不到CreateQtUI函数" << std::endl; FreeLibrary(g_hQtDll); g_hQtDll = nullptr; return; } // 创建Qt UI并嵌入到VC父窗口 g_pQtUI = createQtUI(parentHwnd); // 获取设置回调的函数 SetFunCallBackFunc setFunCallBack = (SetFunCallBackFunc)GetProcAddress(g_hQtDll, "SetFunCallBack"); if (setFunCallBack != nullptr) { // 设置回调函数和测试参数 const wchar_t* param = L"test_shape_param"; setFunCallBack(handleEvent, param); } else { std::cout << "找不到SetFunCallBack函数" << std::endl; } } // 释放资源的函数(VC程序退出时调用) void detachLibrary() { if (g_pQtUI) { delete static_cast<QWinWidget*>(g_pQtUI); g_pQtUI = nullptr; } if (g_hQtDll) { FreeLibrary(g_hQtDll); g_hQtDll = nullptr; } } // 假设你的目标业务函数定义 void elementsExampleCreateShapeActive(WCharCP param) { // 这里写你的业务逻辑 std::wcout << L"调用elementsExampleCreateShapeActive,参数:" << param << std::endl; }
关键注意事项
- 保持DLL加载状态:VC端必须保留Qt DLL的句柄(
g_hQtDll)直到不再需要Qt UI,否则会导致回调崩溃。 - 调用约定一致:所有跨DLL的函数指针都要使用
CALLBACK(即__stdcall),避免栈损坏。 - Qt环境初始化:Qt DLL里要确保
QApplication被正确初始化,QWinWidget是Qt嵌入Win32窗口的核心类。 - 字符编码统一:全程使用
wchar_t/WCharCP避免ANSI和Unicode的编码转换问题,减少乱码风险。
内容的提问来源于stack exchange,提问作者李泽敏




