You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何在基于Qt的UI DLL点击事件中调用VC++ DLL/EXE函数(qtwinmigrate)

实现Qt UI DLL按钮点击触发VC++ DLL函数调用(基于QtWinMigrate)

问题概述

你需要实现的核心需求是:基于QtWinMigrate的Qt UI DLL中,点击pushButton时调用VC++ DLL/EXE中的elementsExampleCreateShapeActive()函数。你的思路是通过回调函数实现跨DLL通信,这个方向是对的,但现有代码存在几个细节问题需要调整才能正常运行。

现有代码的核心问题

  1. VC++端DLL加载后立即释放attachLibrary()里调用FreeLibrary(hDLL)会直接卸载Qt DLL,后续Qt UI触发回调时会因为DLL已不在内存而崩溃。
  2. 回调函数签名不匹配:Qt端的SetFunCallBack是两个参数(回调函数指针+WCharCP参数),但VC端定义的PFunCallBack只接受一个参数,会导致参数传递错误。
  3. 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,提问作者李泽敏

火山引擎 最新活动