I've a Qt application, that must be able to load plugins. When the plugin is loaded (I use boost::dll) I call the plugin factory method for retrieving a class with the followed (simplified) interface:
#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;
};
The interface allows to retrieve a HWND pointer to a window, that then I attach on the application. The basic idea is to create with my application a container where other libraries can be loaded and run.
In a plugin I realize this interface by creating another QApplication. I create a forward declaration so when I load the plugin the application does not know anything about the fact that the plugin is using Qt, creating a opaque pointer:
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
creates a QApplication
and a QMainWindow
, then I run the QApplication::exec()
in another thread. I then create a method for retrieving the HWND
handle of the QMainWindow
, that should be passed through the plugin interface to the container application, that then will show it.
#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();
}
When I run the application, the container loads the plugin, but then I've an assertion in Application.cpp
, at following line:
m_application = std::make_unique<QApplication>(argc, argv);
The assertion says
ASSERT failure in QCoreApplication: "there should be only one application object", file H:\...\corelib\kernel\qcoreapplication.cpp, line 792
Since the container application does now know anything about the content of the plugin (the interface does not expose Qt classes and logic) I was expecting to be able to run both QApplication
.
What causes the problem and how can I solve it?