gpt4 book ai didi

c++ - 加载 Qt 共享库时 Qt 小部件不显示

转载 作者:太空宇宙 更新时间:2023-11-04 12:40:01 25 4
gpt4 key购买 nike

要求:Qt 小部件在 Qt 共享库加载时显示,用于非 Qt 应用程序。

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

  1. 所有的Qt widgets都必须存在于“main thread”中,“main thread”是Qt对象创建的第一个线程。所以,创建一个非 Qt 线程(std::thread),然后创建该线程中的 QApplication 和其他一些小部件应该可以工作,但实际上没有。

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

  3. 线程解决方案不适用于 Mac OS,我的目标平台仅是 Windows,所以没关系。

  4. 在我的例子中,如果应用加载我的 Qt 库,并调用显示小部件的方法,有用。但由于某些原因,调用者无法手动调用我的 lib 方法。

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

源代码在这里:

  • dll主要版本:
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;

两个版本都成功调用了 widgets init 东西,但是 widgets 没有出现。

下面是我如何测试它,使用 Qt 加载库,但是,我使用 Win32 API 加载库的事件也失败了。

#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 进行测试。

// 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 的原因。

Qt 不支持此用例。因此,如果您现在让它发挥作用,则无法保证它在未来也能发挥作用。

你不能像这样同时加载 2 个 dll。

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

关于DllMain

来自 Windows 文档:

Warning

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://learn.microsoft.com/en-us/windows/desktop/dlls/dllmain

和动态链接库最佳实践:

You should never perform the following tasks from within DllMain:

  • Call LoadLibrary or LoadLibraryEx (either directly or indirectly). This can cause a deadlock or a crash.
  • Call GetStringTypeA, GetStringTypeEx, or GetStringTypeW (either directly or indirectly). This can cause a deadlock or a crash.
  • Synchronize with other threads. This can cause a deadlock.
  • Acquire a synchronization object that is owned by code that is waiting to acquire the loader lock. This can cause a deadlock.
  • Initialize COM threads by using CoInitializeEx. Under certain conditions, this function can call LoadLibraryEx.
  • Call the registry functions. These functions are implemented in Advapi32.dll. If Advapi32.dll is not initialized before your DLL, the DLL can access uninitialized memory and cause the process to crash.
  • Call CreateProcess. Creating a process can load another DLL.
  • Call ExitThread. Exiting a thread during DLL detach can cause the loader lock to be acquired again, causing a deadlock or a crash.
  • Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky.
  • Create a named pipe or other named object (Windows 2000 only). In Windows 2000, named objects are provided by the Terminal Services DLL. If this DLL is not initialized, calls to the DLL can cause the process to crash.
  • Use the memory management function from the dynamic C Run-Time (CRT). If the CRT DLL is not initialized, calls to these functions can cause the process to crash.
  • Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized.
  • Use managed code.

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

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

  • Qt 将使用 LoadLibrary 加载插件(至少是 qwindows.dll)。如果您使用任何音频或图像或 sql 数据库,Qt 也会尝试加载相应的插件(例如 qjpeg.dll)。
  • Qt 也可能会尝试访问注册表,特别是如果您使用 native 格式的 QSettings
  • Qt 可以创建线程。特别是如果您使用网络或 Qt Quick。
  • Qt 将使用内存管理功能,如 mallocfree

关于c++ - 加载 Qt 共享库时 Qt 小部件不显示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54667520/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com