加载 Qt 共享库时,Qt 小部件不显示 [英] Qt widgets not show up when Qt shared lib loaded

查看:65
本文介绍了加载 Qt 共享库时,Qt 小部件不显示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要求:Qt 共享库加载时显示 Qt 小部件,适用于 none-Qt 应用程序.

Requirements: Qt widgets show up when Qt shared lib loads, for none-Qt application.

经过一些网络搜索,我发现:

After some web searching, I found:

  1. 所有 Qt 小部件都必须存在于主线程"中,主线程"是 Qt 对象创建的第一个线程.因此,创建一个非 Qt 线程(std::thread),然后创建QApplication 和该线程中的其他一些小部件应该可以工作,但不能.

  1. All Qt widgets must live in "main thread", the "main thread" is the first Qt object created thread. so, create a none-Qt thread (std::thread), then, create QApplication and some other widgets in that thread should work, but not.

在创建 QApplication 之前,不要在该非 Qt 线程中创建任何 Qt 相关对象或调用任何 Qt 相关静态方法.

Do not create any Qt related object or call any Qt related static methods before QApplication created, in that none-Qt thread.

线程解决方案对于 Mac OS 来说是不可移植的,我的目标平台只有 Windows,所以,没关系.

The thread solution is not portable for Mac OS, my target platform is Windows only, so, it does not matter.

就我而言,如果应用程序加载我的 Qt 库,并调用显示小部件的方法,有用.但由于某种原因,调用者无法手动调用我的 lib 方法.

In my case, if app load my Qt lib, and invoke the method for showing widgets, it works. but for some reason, caller can not call my lib method manually.

如果宿主应用程序(加载共享库的应用程序)是 Qt 应用程序,您应该调用 QApplication::processEvents(),而不是 QApplication::exec().就我而言,我应该在该线程中调用 QApplication::exec().

If host application (one that loads the shared lib) is Qt application, you should call QApplication::processEvents(), not QApplication::exec(). in my case, I should call QApplication::exec() in that thread.

源代码在这里:

  • dllMain 版本:
BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID lpReserved)
{
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        auto t = std::thread([]() {
            // setCodecForLocale is in the same thread, 
            // call it before QApplication created should be OK.

            QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
            int i = 0;
            int argc = 0;
            QApplication app(argc, 0);

            auto dialogLogin = new DialogLogin(); // custom widget

            dialogLogin->setModal(true);
            dialogLogin->show();
            app.exec(); // app.processEvents() not work, too.
        });

        t.join(); // wait for thread ends in dllMain should be BAD, test only
    }

    return true;
}

  • 简单的 C++ 静态类版本
  • class LibExecutor {
    public:
        LibExecutor()
        {
            auto t = std::thread([]() {
                QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
                int argc = 0;
                QApplication app(argc, 0);
    
                auto dialogLogin = new DialogLogin();
                dialogLogin->setModal(true);
                dialogLogin->show();
                app.exec();
            });
    
            t.join();
        }
    };
    
    static LibExecutor libExecutor;
    

    两个版本都成功调用了小部件初始化内容,但小部件没有出现.

    Both version invoke widgets init stuff successfully, but widgets not show up.

    这是我测试它的方法,使用 Qt 加载库,但是,如果我使用 Win32 API 加载库,也失败了.

    Here is how I test it, using Qt load lib, but, event I load lib using Win32 API, failed too.

    #include "mainwindow.h"
    #include <QApplication>
    #include <QLibrary>
    
    int main(int argc, char* argv[])
    {
        QLibrary lib("F:/lib_location/lib_name.dll");
    
        if (lib.load()) {
            qDebug() << "load ok!";
        } else {
            qDebug() << "load error!";
        }
    }
    

    推荐答案

    这是一个工作示例.使用 Qt 5.12 和 MSVC2017 以及 MinGW 进行测试.

    Here is a working example. Tested with Qt 5.12 and MSVC2017 and MinGW.

    // main.cpp
    int main(int argc, char *argv[])
    {
        run_mylib_t *f= nullptr;
        HMODULE lib = LoadLibraryA("..\\mylib\\debug\\mylib.dll");
    
        if (!lib) {
            qDebug() << "Failed to load library;";
            return -1;
        }
    
        f = reinterpret_cast<run_mylib_t *>(GetProcAddress(lib, "run_mylib"));
    
        if (!f) {
            qDebug() << "Failed to get function";
            return -1;
        }
    
        f(argc, argv);      
    
        return 0;
    }
    
    // mylib.h
    extern "C" MYLIBSHARED_EXPORT int run_mylib(int argc, char *argv[]);
    using run_mylib_t = int(int, char *[]);
    
    // mylib.cpp
    int loop(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    
    int run_mylib(int argc, char *argv[])
    {
        auto lambda = [argc, argv]() {loop(argc, argv); };
    
        std::thread thread(lambda);
        thread.join();
    
        return 0;
    }
    

    注意,如果在线程创建之前使用Qt函数,Qt会检测到它不在主线程中,进程会崩溃.这就是为什么我不使用 QLibrary.

    Note that if you use Qt function before the thread is created, Qt will detect that it is not in the main thread and the process will crash. That is why I do not use QLibrary.

    Qt 不支持此用例.所以,如果你现在让它工作,你不能保证它将来会工作.

    This use case is not supported by Qt. So if you make it work now, you are not guaranteed that it will work in the future.

    您不能像这样同时加载 2 个 dll.

    You cannot load at the same time 2 dll like this.

    根据您在主应用程序中执行的操作,可能会发生某些 Qt 功能未按预期工作的情况.例如,Qt 可能会期望来自 Windows 的消息,但永远不会得到它们,因为它们将由真正的主线程处理.

    Depending on what you do in your main application, it could happens that some Qt features are not working as expected. For instance it could happen that Qt expects messages from Windows, but will never get them because they will be handled by the real main thread.

    来自 Windows 文档:

    From Windows documentation:

    警告

    您可以在 DLL 条目中安全执行的操作有很大限制观点.请参阅适用于特定 Windows API 的一般最佳实践在 DllMain 中调用不安全.如果你只需要最简单的东西初始化然后在 DLL 的初始化函数中执行此操作.您可以要求应用程序在之后调用初始化函数在调用 DLL 中的任何其他函数之前,DllMain 已运行.

    There are significant limits on what you can safely do in a DLL entry point. See General Best Practices for specific Windows APIs that are unsafe to call in DllMain. If you need anything but the simplest initialization then do that in an initialization function for the DLL. You can require applications to call the initialization function after DllMain has run and before they call any other functions in the DLL.

    -- https://docs.microsoft.com/en-us/windows/desktop/dlls/dllmain

    和动态链接库最佳实践:

    and Dynamic-Link Library Best Practices:

    您不应在 DllMain 中执行以下任务:

    You should never perform the following tasks from within DllMain:

    • 调用 LoadLibrary 或 LoadLibraryEx(直接或间接).这可能会导致死锁或崩溃.
    • 调用 GetStringTypeA、GetStringTypeEx 或 GetStringTypeW(直接或间接).这可能会导致死锁或崩溃.
    • 与其他线程同步.这可能会导致死锁.
    • 获取等待获取加载器锁的代码拥有的同步对象.这可能会导致死锁.
    • 使用 CoInitializeEx 初始化 COM 线程.在某些条件下,该函数可以调用 LoadLibraryEx.
    • 调用注册表函数.这些函数在 Advapi32.dll 中实现.如果 Advapi32.dll 未在您的 DLL 之前初始化,则DLL 可以访问未初始化的内存并导致进程崩溃.
    • 调用 CreateProcess.创建进程可以加载另一个 DLL.
    • 调用 ExitThread.在 DLL 分离期间退出线程可能会导致再次获取加载程序锁,从而导致死锁或崩溃.
    • 调用 CreateThread.如果不与其他线程同步,创建一个线程是可行的,但有风险.
    • 创建命名管道或其他命名对象(仅限 Windows 2000).在 Windows 2000 中,命名对象由终端服务 DLL 提供.如果此 DLL 未初始化,则对该 DLL 的调用可能导致进程崩溃.
    • 使用动态 C 运行时 (CRT) 的内存管理功能.如果 CRT DLL 未初始化,则调用这些函数可以导致进程崩溃.
    • 调用 User32.dll 或 Gdi32.dll 中的函数.某些函数会加载另一个可能未初始化的 DLL.
    • 使用托管代码.

    -- https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-best-practices

    由此我可以告诉您,您将无法创建 QApplication 并从 DllMain 运行 Qt 应用程序,原因至少如下:

    From this I can tell you that you will not be able to create a QApplication and run a Qt app from DllMain for, at least, the following reasons:

    • Qt 将使用 LoadLibrary 加载插件(至少是 qwindows.dll).如果您使用任何音频或图像或 sql 数据库,Qt 也会尝试加载相应的插件(例如 qjpeg.dll).
    • Qt 也可能会尝试访问注册表,尤其是当您使用具有本机格式的 QSettings 时.
    • Qt 可能会创建线程.特别是如果您使用网络或 Qt Quick.
    • Qt 将使用诸如 mallocfree 之类的内存管理功能.
    • Qt will load plugins (at least qwindows.dll) using LoadLibrary. If you use any audio or image or sql database, Qt will also try to load the corresponding plugins (e.g. qjpeg.dll).
    • Qt might also try to access the registry, in particular if you use QSettings with native format.
    • Qt may create threads. In particular if you use network or Qt Quick.
    • Qt will use memory management functions like malloc or free.

    这篇关于加载 Qt 共享库时,Qt 小部件不显示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆