[...] 呼び出し元のアプリケーションが Qt ではない場合、たとえば C# の場合、呼び出し元のアプリケーション (別名メイン スレッド) には Qt 固有のライブラリを呼び出す機能がないため、イベント ループが埋め込まれた DLL を設計する必要があります。その中に。
これは正確ではありません。Windows では、スレッドごとに 1 つのイベント ループが必要です。そのイベント ループは、純粋な WINAPI、C#、または必要な言語/フレームワークを使用して実装できます。そのイベント ループが Windows メッセージをディスパッチしている限り、Qt コードは機能します。
存在する必要がある唯一の Qt 固有のものは、メイン スレッドから作成されたQApplication
(またはQGuiApplication
、必要に応じて ) のインスタンスです。QCoreApplication
ネイティブ コード (メイン アプリケーション) は既に Windows メッセージを送信しているため、そのインスタンスを呼び出してはなりません。アプリケーションインスタンスを作成した後、それを「準備」するために一度exec()
呼び出す必要があります。そうする必要があるのはバグ(省略)であり、Qt 5.5でも必要かどうかはわかりません。QCoreApplication::processEvents
その後、ネイティブ アプリケーションの GUI スレッドは、ネイティブ イベントを Qt ウィジェットおよびオブジェクトに適切にディスパッチします。
unaltered を使用して作成したワーカー スレッドはQThread::run
ネイティブ イベント ループをスピンし、これらの各スレッドはネイティブ オブジェクト (Windows ハンドル) などをホストQObject
し、非同期プロシージャ コールを実行できます。
すべてを設定する最も簡単な方法は、initialize
Qt を起動するためにメイン アプリケーションによって一度呼び出される関数を DLLに提供することです。
static int argc = 1;
static char arg0[] = "";
static char * argv[] = { arg0, nullptr };
Q_GLOBAL_STATIC_WITH_ARGS(QApplication, app, (arc, argv))
extern "C" __declspec(dllexport) void initialize() {
app->processEvents(); // prime the application instance
new MyWindow(app)->show();
}
初期化を行わないという要件は、 Qt に固有DllMain
のものではありません。ネイティブの WINAPI を使用するコードは、ほとんど何もすることが禁止されています。ウィンドウなどを作成することはできません。DllMain
からメモリ、ウィンドウ ハンドル、スレッドなどを割り当てる可能性のある操作を行うのはエラーであることを繰り返しますDllMain
。kernel32
一部の例外を除いて、APIのみを呼び出すことができます。QThread
orQApplication
インスタンスを割り当てることは、明らかな no-noです。「現在の」(ランダムな) スレッドからの APC 呼び出しをキューに入れることができる最善の方法ですが、スレッドが APC を実行するのに十分な時間存続するか、または APC が走るチャンスを得ることができます。
この回答 によると、冒険好きならinitialize()
、APC としてへの呼び出しをキューに入れることができます。DllMain
その場合の主な問題は、 が正しいスレッドから呼び出されていることを確認できないことです。呼び出し元のスレッドは、アラート可能な待機状態になる必要があります (メッセージ ループのポンピングなど)。その後、専用のアプリケーション スレッドを作成できますが、新しいスレッドの代わりに使用する必要がある特定の「メイン」スレッドが他にあるかどうかを判断することはできません。スレッド ハンドルはデタッチする必要があるため、std::thread
代わりに を使用する必要がありますQThread
。
void guiWorker() {
int argc = 1;
const char dummy[] = "";
char * argv[] = { const_cast<char*>(dummy), 0 };
QApplication app(argc, argv);
QLabel label("Hello, World!");
label.show();
app.exec();
}
VOID CALLBACK Start(_In_ ULONG_PTR) {
std::thread thread { guiWorker };
thread.detach();
}
BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID)
{
switch (reason) {
case DLL_PROCESS_ATTACH:
QueueUserAPC(Start, GetCurrentThread(), NULL);
break;
case DLL_PROCESS_DETACH:
// Reasonably safe, doesn't allocate
if (QCoreApplication::instance()) QCoreApplication::instance()->quit();
break;
}
}
一般的に言えば、そのようなコードは必要ありません。メイン アプリケーションは、イベント ポンプ (通常はメイン スレッド) を使用してスレッドから初期化関数を呼び出す必要があり、ネイティブ機能のみを使用して DLL を初期化する場合と同様に、すべてが機能します。