読者です 読者をやめる 読者になる 読者になる

ゆとりーなの日記

日記的な事を書いて行くと思はれる

COMのシングルトンを作る時の罠

今日も華麗にCOMに嵌められたのでメモ。
畫像の讀込󠄁みにWicAPIを使󠄁ふ場合、IWICImagingFactoryは1つあれば十分󠄁さうなのでシングルトンにしたいなと思ふのは人情󠄁である。この時IWICImagingFactory *はスマポで當然管理する事になる。

class wic_imaging_factory_singleton {
  wic_imaging_factory_singleton &get_instance() {
    static wic_imaging_factory_singleton instance;
    return instance;
  }

  wic_imaging_factory_singleton(const wic_imaging_factory_singleton &) = delete;
  wic_imaging_factory_singleton(wic_imaging_factory_singleton &&) = delete;
  wic_imaging_factory_singleton &operator =(const wic_imaging_factory_singleton &) = delete;
  wic_imaging_factory_singleton &operator =(wic_imaging_factory_singleton &&) = delete;
 
private:
  wic_imaging_factory_singleton() {
    if (FAILED(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, reinterpret_cast<LPVOID *>(&factory)))) {
      throw std::runtime_error{"WICImagingFactoryの初期化に失敗"};
    }
  }
  com_ptr<IWICImagingFactory> factory;
};

WicAPIはホンマモンのCOMなので使󠄁ふ前󠄁にCoInitializeExの呼び出しが必要󠄁である。CoInitializeExは解放するCoUninitialize()と組で使󠄁ふものなのでRAIIクラスを作つて管理するのが常道󠄁であらう。

struct com_initializer {
  com_initializer() {
    if (FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)) {
      throw std::runtime_error{"COMの初期化に失敗"};
    }
  }

  ~com_initializer() {CoUninitialize();}
  com_initializer(const com_initializer &) = delete;
  com_initializer(com_initializer &&) = delete;
  com_initializer &operator =(const com_initializer &) = delete;
  com_initializer &operator =(com_initializer &&) = delete;
};

これらを實際に使󠄁つてみる。恐らく次󠄁の樣なコードになるだらう。

int main() {
  com_initializer init;
  wic_imaging_factory_singleton::get_instance();
}

これを實行してみると、com_ptrのデストラクタでこける。要󠄁はIWICImagingFactoryのReleaseで例外が飛ぶのである。
原因は氣附いてみれば簡單で、自動局所󠄁變數であるcom_initializerの方が靜域變數のwic_imaging_factory_singletonよりも先にお亡くなりになるからである。CoUninitialize()の後にIWICImagingFactoryをReleaseしに行く訣だからコケるのもまあ分󠄁る。この邊りの順番には氣を附けないと酷󠄁い目に遭󠄁ふ。