У меня есть приложение Qt, которое должно загружать плагины. Когда плагин загружен (я использую boost :: dll), я вызываю метод фабрики плагинов для извлечения класса с последующим (упрощенным) интерфейсом:
#ifdef _WIN32
using WindowHandle = HWND;
#else
using WindowHandle = void*;
#endif
class PluginInterface {
public:
/**
* @brief Retrieve the window handle of the plugin.
*
* The plugin should have a single main window, that's the main window of the
* plugin itself. The method retrieve this window handler and pass it to the
* caller, in order to allow it to attach this window to its main gui
* interface. The pointer ownership is on the plugin, so the caller mustn't
* delete it for any reason.
*
* @return Window handle.
*/
virtual WindowHandle getWindowHandle() const = 0;
};
Интерфейс позволяет получить HWND-указатель на окно, которое затем прикрепляю к заявке. Основная идея c заключается в создании с моим приложением контейнера, в который можно загружать и запускать другие библиотеки.
В плагине я реализую этот интерфейс, создав еще одно приложение QApplication. Я создаю предварительную декларацию, чтобы при загрузке плагина приложение ничего не знало о том, что плагин использует Qt, создавая непрозрачный указатель:
class Application;
class MyPluginInterface final : public PluginInterface {
public:
MyPluginInterface();
virtual ~MyPluginInterface();
public:
WindowHandle getWindowHandle() const override;
private:
Application* m_application;
};
////// CPP file
#include "Application.hpp"
MyPluginInterface::MyPluginInterface() :
PluginInterface() {
m_application = new Application();
}
MyPluginInterface ::~MyPluginInterface () {
}
WindowHandle MyPluginInterface ::getWindowHandle() const {
return m_application->getWindowHandle();
}
Application
создает QApplication
и QMainWindow
, тогда я запускаю QApplication::exec()
в другом потоке. Затем я создаю метод для извлечения дескриптора HWND
для QMainWindow
, который должен быть передан через интерфейс плагина к приложению-контейнеру, который затем будет отображать его.
#ifndef APPLICATION_HPP_
#define APPLICATION_HPP_
#include "WindowHandle.hpp"
#include <QApplication>
#include <QMainWindow>
#include <memory>
#include <thread>
class Application final {
public:
Application();
~Application();
WindowHandle getWindowHandle() const;
private:
void createMainWindow();
private:
std::unique_ptr<QApplication> m_application;
std::thread m_applicationThread;
QMainWindow* m_mainWindow;
};
#endif // !APPLICATION_HPP_
/////////////// CPP FILE
#include "Application.hpp"
#include <qpa/qplatformnativeinterface.h>
#include <QLabel>
///////////////////////////////////////////////////////////////////////////////
// USING SECTION //
///////////////////////////////////////////////////////////////////////////////
using WindowHandle;
using std::thread;
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION //
///////////////////////////////////////////////////////////////////////////////
Application::Application() {
int argc = 1;
char* argv[] = { "plugin" };
QCoreApplication::addLibraryPath("./");
m_application = std::make_unique<QApplication>(argc, argv);
m_applicationThread = thread([this]() {
createMainWindow();
m_application->exec();
});
}
Application::~Application() {
if (m_applicationThread.joinable()) {
m_applicationThread.join();
}
}
WindowHandle Application::getWindowHandle() const {
WindowHandle handle{ nullptr };
if (QWindow* w = m_mainWindow->windowHandle()) {
auto nativeInterface = QGuiApplication::platformNativeInterface();
handle = static_cast<HWND>(nativeInterface->nativeResourceForWindow(QByteArrayLiteral("handle"), w));
}
return handle;
}
///////////////////////////////////////////////////////////////////////////////
// PRIVATE SECTION //
///////////////////////////////////////////////////////////////////////////////
void Application::createMainWindow() {
m_mainWindow = new QMainWindow();
QLabel* label = new QLabel(m_mainWindow);
label->setText("Questo e' un plugin");
m_mainWindow->setCentralWidget(label);
m_mainWindow->show();
}
Когда я запускаю приложение, контейнер загружает плагин, но затем у меня есть утверждение в Application.cpp
, в следующей строке:
m_application = std::make_unique<QApplication>(argc, argv);
Утверждение говорит:
ASSERT failure in QCoreApplication: "there should be only one application object", file H:\...\corelib\kernel\qcoreapplication.cpp, line 792
Поскольку приложение контейнера делает теперь знаю что-нибудь о содержимом плагина (интерфейс не предоставляет классы Qt и логи c) Я ожидал, что смогу запустить оба QApplication
.
Что вызывает проблему и как я могу решить это?