effekseer/EffekseerForUnity

Calling CoInitializeEx from DllMain may cause unexpected behavior

Closed this issue · 0 comments

概要

EffekseerPlugin.cppDllMain 関数の中で、CoInitializeEx(NULL, COINIT_MULTITHREADED)CoUninitialize() が呼び出されていますが、これが予期しない動作を引き起こす可能性があります。

説明するのがとても難しいので、Microsoft のドキュメントのリンクをいくつかあげますので、ご覧ください。

具体的に発生した問題

  • 再現率が 100% ではないため、必ずしも CoInitializeEx が原因とは言い切れませんが、しばしば発生している問題を書きます。

  • UnityEditor において、EffekseerForUnity プラグインアセットを追加したプロジェクトで、ファイルダイアログを開く操作 (シーンを開くなど) を実行すると、ダイアログが表示されず、Editor の操作ができなくなることがあります。

再現手順

  1. UnityEditor を起動し、エフェクトの設定をしたシーンを開きます。

  2. Hierarchy から Effekseer Emitter コンポーネントが設定されているオブジェクトを選択し、Scene View に Effekseer の GUI を表示します。

  3. 数分待ちます。

  4. UnityEditor に戻り、ファイルダイアログ(Open Scene, Save as Scene, Create New Animation など)を開く操作をすると、ダイアログが表示されず、Editor の操作ができなくなることがあります。

必ず再現するわけではありませんが、しばらく作業したのち、ファイルダイアログを開こうとしたときに、しばしば発生します。

再現環境

  • OS: Windows 10 1903 64bit (クリーンインストールした環境で再現)

  • Unity: 2018.4.5f1, 2019.2.9f1, Unity 2017.4.32f1

  • Effekseer, EffekseerForUnity: 2019-10-15 時点の master ブランチをビルド。

  • Visual Studio 2019

根拠

  • EffekseerPlugin.cpp と同様に、DllMain 関数の中で、CoInitializeEx(NULL, COINIT_MULTITHREADED)CoUninitialize() を実行する、ダミーの Unity プラグインを作成したところ、ファイルダイアログが表示されず、Editor の操作ができなくなる現象が再現しました。

  • CoInitializeExCoUninitialize をコメントアウトすると、再現しなくなるようです。

  • ドキュメントのリンクにあげた中で、マルチスレッドアパートメントから GetOpenFileName/GetSaveFileName を呼び出したときの問題が述べられており、これが該当するかもしれません。

ダミーの Unity プラグインのコード

#include "uni-dummy-coinit.h"

UNI_DUMMY_COINIT_API int UNI_PLUGIN_API DummyFunc(int a, int b) {
    ::OutputDebugString(L"DummyFunc called\n");
    return a + b;
}

BOOL APIENTRY DllMain(
    HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
) {
    HRESULT hr;
    wchar_t buffer[256];

    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
        hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
        ::_snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), _TRUNCATE, L"DLL_PROCESS_ATTACH, hr = 0x%08X\n", hr);
        ::OutputDebugString(buffer);
        break;
    case DLL_THREAD_ATTACH:
        hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
        ::_snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), _TRUNCATE, L"DLL_THREAD_ATTACH, hr = 0x%08X\n", hr);
        ::OutputDebugString(buffer);
        break;
    case DLL_THREAD_DETACH:
        ::CoUninitialize();
        ::OutputDebugString(L"DLL_THREAD_DETACH\n");
        break;
    case DLL_PROCESS_DETACH:
        ::CoUninitialize();
        ::OutputDebugString(L"DLL_PROCESS_DETACH\n");
        break;
    }
    return TRUE;
}