0
0

[初級] C++: COMスマートポインタの基本

Last updated at Posted at 2024-09-22

COM スマートポインタの基本
C++、COM、_com_ptr_t

本記事の前提条件は以下の通りです。

  • 初心者向け
  • とは言っても、何らかのプログラムはそれなりに書けるけど、C とか C++ はちょっと、という人向け
  • ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
  • Visual Studio 2013 くらい~
  • Windowsプログラム (CUI, GUI)
  • コードの検証
    • 開発環境: Visual Studio 2022, x64, Release ビルド
    • 実行環境: Windows 10
  • 本記事は上から順番に読む前提となっている
  • 「Visual Studio 2013 くらい~」と書いてあるが、本記事の内容だと現在過去未来のどのバージョンでもほぼ同じ

COM スマートポインタを使う

_com_ptr_tを使うわけだが、そのまま使おうとすると、

c++
#include <comdef.h>
_com_ptr_t<_com_IIID<IPlayback, &__uuidof(IPlayback)>> pPlayback;
pPlayback.CreateInstance(...);

と、ややこしいので、素直に .tlh にある _COM_SMARTPTR_TYPEDEF マクロを利用する。
_COM_SMARTPTR_TYPEDEF マクロは、インターフェースが IPlayback であれば、IPlaybackPtr というスマートポインタを定義してくれるので、以下のように書けるようになる。

c++
#include <comdef.h>
IPlaybackPtr pPlayback;
pPlayback.CreateInstance(CLSID_MusicPlayer, NULL, CLSCTX_INPROC_SERVER);
pPlayback->Play();
pPlayback->Stop();

メリット

RAII によるリソース管理

Release の呼び出しは COM スマートポインタが RAII に基づいて処理してくれるので不要になる。

c++
#include <comdef.h>
IPlaybackPtr pPlayback;
pPlayback.CreateInstance(CLSID_MusicPlayer, NULL, CLSCTX_INPROC_SERVER);
pPlayback->Play();
pPlayback->Stop();
// pPlaybackがスコープを抜ける時に自動的にReleaseされる
//pPlayback->Release();

不要になるというか、明示的に呼び出すと参照カウントが異常になるので、注意が必要。

キャストによる COM インターフェース取得

COM スマートポインタのキャスト(コピーコンストラクタ、コピー代入演算子、型変換演算子)はすべて QueryInterface の呼び出しによる COM インターフェースの取得処理となる。

すべて、と言っても、あくまでも、COM スマートポインタから 別の型の COM スマートポインタにキャストする場合。

したがって、COM スマートポインタを使わない場合に下記のように書いていたものが、

c++
	IPlayback* pPlayback;
	CoCreateInstance(CLSID_MusicPlayer,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IPlayback,
		(void**)&pPlayback);

	pPlayback->Play();
	pPlayback->Stop();

	IVolumeControl* pVolumeControl;
	pPlayback->QueryInterface(IID_IVolumeControl, (void**)&pVolumeControl);

	pVolumeControl->Up();
	pVolumeControl->Down();
	pVolumeControl->Release();

	pPlayback->Release();

次のように書けることになる。

c++
	IPlaybackPtr pPlayback;
	pPlayback.CreateInstance(CLSID_MusicPlayer,
		NULL,
		CLSCTX_INPROC_SERVER);

	pPlayback->Play();
	pPlayback->Stop();

	IVolumeControlPtr pVolumeControl{ pPlayback };

	pVolumeControl->Up();
	pVolumeControl->Down();

便利である。

便利ではあるが、QueryInterface を考えればすぐわかるが、いつキャストが失敗するかわからない、ということと、_com_ptr_t 自体がテンプレートクラスであり、コンパイル時、いかなる COM インターフェース間のキャストであっても呼び出しを解決してコンパイル出来てしまう、というところに注意が必要である。

以上。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0