VC++ COMの呼び出し

Visual C++ Express 2010で、ATLを使わずに、COMクライアントを作成してみた。

今回は、iTunesから現在再生中の曲のタイトルを取得するクライアントを例に説明する。

最初に、#importを使ってiTunesのインターフェスの宣言を取り込む

書き方は以下のとおり。

#import "c:\\program files\\itunes\\iTunes.exe" named_guids raw_interface_only

続いて、COMの初期化と終了処理。ここは、お決まりのCoInitialize() CoUninitialize()を書く

int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(0);
// ここにCOM呼び出しの処理を書く
::CoUninitialize();
}

最初は、ラッパークラスなどを使わない方法のコードを示す。

void iTunes1(){
HRESULT hResult;
iTunesLib::IiTunes *pItunes = NULL;
iTunesLib::IITTrack *pTrack = NULL;
BSTR name = NULL;
char buf[256];

//インスタンスの作成
hResult = ::CoCreateInstance(iTunesLib::CLSID_iTunesApp, NULL, CLSCTX_LOCAL_SERVER,
iTunesLib::IID_IiTunes, (LPVOID *)&pItunes);

if( ! SUCCEEDED(hResult) ) {
printf("ERROR occured = %08x\n", hResult);
goto end;
}

//トラック取得
hResult = pItunes->get_CurrentTrack(&pTrack);
if( ! SUCCEEDED(hResult) ) {
printf("ERROR occured = %08x\n", hResult);
goto end;
}

if (pTrack == NULL){
printf("no track\n");
goto end;
}

//トラックの名前
hResult = pTrack->get_Name(&name);
if( ! SUCCEEDED(hResult) ) {
printf("ERROR occured = %08x\n", hResult);
goto end;
}

// UNICODEANSI変換
::WideCharToMultiByte(CP_ACP,0,(LPCWSTR)name,-1,buf,sizeof(buf),NULL,NULL);

//コンソールに表示
printf("%s\n", buf);

end:
// BSTR開放
if (name != NULL) ::SysFreeString(name);

// track開放
if (pTrack != NULL) pTrack->Release();

// pItues開放
if (pItunes != NULL) pItunes->Release();
}

この方法では、いかにもCOMぽいのだが、以下の点は、より簡単に書くことができる。


  1. リソースの解放
    不要になったオブジェクトは Release()関数の呼び出して解放が必要。COMのお作法だが、いかにも忘れやすい。
  2. BSTRの扱い
    文字列を扱うBSTRは、UNICODEで文字列が格納されているため、UNICODEANSI変換が必要になる。もちろん、プログラム内で文字列をすべてUNICODEで扱っていれば問題ない。
    そして、使い終わった後はSysFreeString()関数でリソースを解放してやる必要がある。
  3. 関数の呼び出しが面倒
    get_XXX()の引数にout変数を渡している。これでも良いが、リターン値で扱いたい。
  4. エラー処理
    毎回、関数の戻り値(HRESULT)をチェックして、成功失敗を判断している

これらを行うには、COMのラッパークラスとCOMの便利なクラス(_bstr_t, _com_error)を使う。

まず、ラッパークラスは、最初の#import宣言のところのraw_interface_only を外すと、iTunesのCOMインターフェスのラッパークラスを生成してくれる。

#import "c:\\program files\\itunes\\iTunes.exe" named_guids

ただし、iTunesの場合、このままだと、GetFreeSpaceという関数が、Windows.hのマクロとぶつかってしまうので、以下のようにする。

#undef GetFreeSpace //Windows.h のマクロとぶつかる
#import "c:\\program files\\itunes\\iTunes.exe" named_guids

こうすると、IiTunesのスマートポインタ版のIiTunesPtrが作成される最初の変数宣言は以下のとおり。

iTunesLib::IiTunesPtr iTunesObj;

CoCreateInstanceをラップしたCreateInstance関数が使える。こちらのほうが引数が少なくて便利。

また、get_CurrentTrackはCurrentTrackにより参照できる。

ラッパークラスを使うことで以下のようにコードを書くことができる。

iTunesLib::IiTunesPtr iTunesObj; //スマートポインタなのでRelease不要
iTunesObj.CreateInstance(iTunesLib::CLSID_iTunesApp);

iTunesLib::IITTrackPtr track; //スマートポインタなのでRelease不要

if (track == iTunesObj->CurrentTrack) {
printf("no track\n");
return;
}


続きてBSTRは、ラッパークラスの_bstr_tを使うことで以下のように扱える

_bstr_t bstrName;	// _bstr_t はBSTR型のスマートポインタ版
track->get_Name(bstrName.GetAddress());

// _bstr_tは (char*)にキャストするとUNICODEからANSIに変換する
printf("%s\n", (char*)bstrName);


エラー処理は _com_errorクラスを使うと、try〜catchで書くことができる。

try {
//COMの処理
}catch (_com_error& e) {
// 例外ハンドラ
printf("Error: %s\n", e.ErrorMessage());
}


以上の記述方法を使うと、最初に示したコードは以下のようになる

// SimpleComClient.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include <windows.h>

#include <stdio.h>
#include <tchar.h>
#include <string>

#undef GetFreeSpace //Windows.h のマクロとぶつかる
#import "c:\\program files\\itunes\\iTunes.exe" named_guids

// 便利ラッパーを使った例
void iTunes(){
iTunesLib::IiTunesPtr iTunesObj; //スマートポインタなのでRelease不要

try {
iTunesObj.CreateInstance(iTunesLib::CLSID_iTunesApp);

iTunesLib::IITTrackPtr track; //スマートポインタなのでRelease不要

if (track == iTunesObj->CurrentTrack) {
printf("no track\n");
return;
}

// _bstr_tは (char*)にキャストするとUNICODEからANSIに変換する
printf("%s\n", (char*)iTunesObj->CurrentTrack->Name);
}catch (_com_error& e) {
// 例外ハンドラ
printf("Error: %s\n", e.ErrorMessage());
}
}

// メイン処理
int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(0);

iTunes();

::CoUninitialize();
}

以上のように、ATLを使わなくても簡単にCOMクライアントを書くことができた。

この記事意外にもVC++のCOM向けマイクロソフト拡張は以下のものがある

COMサポート関数


_com_raise_error


Throws a _com_error in response to a failure.


ConvertBSTRToString


Converts a BSTR value to a char *.


ConvertStringToBSTR


Converts a char * value to a BSTR.

 

COMサポートクラス


_bstr_t


Wraps the BSTR type to provide useful operators and methods.


_com_error


Defines the error object thrown by _com_raise_error in most failures.


_com_ptr_t


Encapsulates COM interface pointers, and automates the required calls to AddRef, Release, and QueryInterface.

_variant_t

Wraps the VARIANT type to provide useful operators and methods.