青島のしま〜Blue Islands〜


VCでActiveXコンポーネント(COMも含む)のイベントを扱う

VCでCOMのイベントを受信するには,
イベントシンクを実装する必要があります.

サンプルを作ってみたので,以下にソースを載せます.
このサンプル作成に当たっては,以下のサイトを参考にしました.

なお,開発環境はVC++6.0を用いています.
.NET2003でも,多少操作方法は違いますが同等のことができるようです.

ActiveXの作成

VC++6.0の場合

  • プロジェクトの新規作成で,「ATL COM AppWizard」を選択
  • メニューの「挿入」→「ATLオブジェクトの新規作成」
    • オブジェクトの「シンプルオブジェクト」
    • ※ここでは,ショートネームに「Counter」とした.
    • ※「アトリビュート」タブのほうの,「コネクションポイントのサポート」にチェックするのを忘れずに
  • そうすると,ClassViewに,鍵のマークのICounterというのが追加される.
    • これを右クリックして,メソッドの追加やプロパティの追加を行う
    • ※ここでは,プロパティ「value」とメソッド「inc()」「dec()」を追加した
  • さらに,もう1つの鍵のマークの「_ICounterEvents」を右クリックして,メソッドを追加する(イベントのメソッドとなる)
    • ※ここでは,OnChange(ICounter*)メソッドを追加
  • 次に,CCounterを右クリックして「接続ポイントのインプリメント」を行う
    • 一度ビルドすると,「CProxy_ICounterEvents<class T>」というクラスができる.
    • ※そのクラスの中に,Fire_OnChange()というメソッドがある.これを呼び出すとイベントを発生させられる.
  • 変数やメソッドの中身を実装する..(上記の参考サイトを参考にどうぞ..)
  • ビルドして,dllとtlbの完成!!

VC++.NETの場合

  • プロジェクトの新規作成で,「ATL プロジェクト?」を選択
  • クラスビューのプロジェクト名を右クリック,追加→クラス→「ATL コントロール」or「ATL シンプルオブジェクト」
  • あとはクラスビューから,作成したATLオブジェクトに対して,関数などを追加していけばOK

ActiveXを扱うVCプログラム(イベント受信を含む)の作成

上で作成したActiveXについて,
以下のような感じで,イベントの受信を実装しました.
※<と>を半角に置き換えてみてください

////////////////////////////////////////////////
// ComLib.h
// COMを使うときに便利な関数とクラスがあります.
////////////////////////////////////////////////

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

#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>

/**
 * 戻り値をチェックし,だめならば例外を投げる
 */
void ComAssert(HRESULT result, const std::string& msg);


/**
 * COM初期化クラス
 * 使い方:このクラスインスタンスを作成したらCOMの初期化を行ってくれます.
 *         また,インスタンスが消滅するときにCOMの終了処理をしてくれます.
 */
class CComInitializer 
{

public:
	/** コンストラクタ */
	CComInitializer();

	/** デストラクタ */
	~CComInitializer();

private:
	/** 初期化する */
	HRESULT init();

};

////////////////////////////////////////////////
// ComLib.cpp
////////////////////////////////////////////////

#include "stdafx.h"
#include "ComLib.h"

//////////////////////////////////////////////////////
// *** グローバル変数 ********************************

CComModule _Module; // モジュール
CComInitializer g_comInit; // COMの初期化に使う
bool g_initialized = false; // COMが初期化されたかを示す


//////////////////////////////////////////////////////
// *** グローバル関数 ********************************

/**
 * 戻り値をチェックし,だめなら例外を投げる
 */
void ComAssert(HRESULT result, const std::string& msg) 
{
	if ( FAILED(result) )
		throw std::runtime_error(msg);
}

//////////////////////////////////////////////////////
// *** CComInitializerクラスのメソッド実装 ***********

// コンストラクタ
CComInitializer::CComInitializer()
{ 
	HRESULT hr = init();
	::ComAssert(hr, "COMの初期化に失敗");
}

// デストラクタ
CComInitializer::~CComInitializer() 
{
	if ( g_initialized ) { // COMが初期化されていたら
		_Module.Term(); // モジュールの後始末
		::CoUninitialize(); // COMの後始末
	}
}

// COMの初期化
HRESULT CComInitializer::init()
{
	HRESULT hr = S_OK;
	if (!g_initialized) { // COMがまだ初期化されていなければ
		hr = ::CoInitialize(NULL);
		g_initialized = SUCCEEDED(hr);
		if ( g_initialized ) { // COMの初期化に成功したら
			_Module.Init(NULL, ::GetModuleHandle(NULL)); // モジュールの初期化
		}
	}
	return hr;
}

////////////////////////////////////////////////
// ComCounter.h
// カウンタCOMのイベントを受信するために使う
////////////////////////////////////////////////
#include "stdafx.h"
#include <iostream>
#include "ComLib.h"

#import "D:/home/Visual Studio Projects/CppSamples/ActiveXTest/Debug/ActiveXTest.dll" no_namespace, named_guids

// カウンタCOMのコントロールID
#define SINKID_COUNTEREVENTS 0

/* CounterEvents : シンク(イベント受付窓口)クラス
 */
class ATL_NO_VTABLE CCounterEvents :
	public CComObjectRootEx<CComSingleThreadModel>,
	public IDispEventImpl<SINKID_COUNTEREVENTS, CCounterEvents, 
						&DIID__ICounterEvents, &LIBID_ACTIVEXTESTLib, 1, 0>
{
private:
	IUnknown*	pConnectTo;		// 接続先オブジェクト
	DWORD		dwCookie;		// 接続を識別するクッキー
	CComPtr<IUnknown>	pEventsUnk; // シンクオブジェクト

public:
	CCounterEvents() {}

BEGIN_COM_MAP(CCounterEvents)
	COM_INTERFACE_ENTRY_IID(DIID__ICounterEvents, CCounterEvents)
END_COM_MAP()

BEGIN_SINK_MAP(CCounterEvents)
	SINK_ENTRY_EX(SINKID_COUNTEREVENTS, DIID__ICounterEvents, 0x1, OnChange)
END_SINK_MAP()

public:
	// 接続
	HRESULT ConnectTo(IUnknown* pUnkCP);
	// 切断
	HRESULT Disconnect();

protected:
	// changeイベント
	HRESULT _stdcall OnChange(ICounter* pCounter);

};

////////////////////////////////////////////////
// ComCounter.cpp
////////////////////////////////////////////////

#include "stdafx.h"
#include "ComCounter.h"


//////////////////////////////////////////////////////
// *** CCounterEventsクラスのメソッド実装 ***********

HRESULT CCounterEvents::ConnectTo(IUnknown* pUnkCP)
{
	//シンクオブジェクトの作成
	QueryInterface(IID_IUnknown, (void**)&pEventsUnk);

	//シンクオブジェクトの接続
	pConnectTo = pUnkCP;
	HRESULT hr;
	hr = AtlAdvise(pConnectTo, pEventsUnk, DIID__ICounterEvents, &dwCookie);
	
	//DIID_を得る部分を仮想関数にでもすれば、
	//ConnectToを1つの親クラスにまとめるのは容易でしょう

	pEventsUnk = 0;
	return hr;
}

HRESULT CCounterEvents::Disconnect()
{
	HRESULT hr;
	hr = AtlUnadvise(pConnectTo, DIID__ICounterEvents, dwCookie);
	//pConnectTo = 0;
	return hr;
}

HRESULT _stdcall CCounterEvents::OnChange(ICounter* pCounter)
{
	// TODO:イベント受信時の処理をここに記述する
	HRESULT hr;
	long lValue = 0;
	hr = pCounter->get_value(&lValue);
	ComAssert(hr,"get_value failed in changed event handler");
	std::cout << "update to " << lValue << std::endl;

	return hr;
}

// メイン

#include "stdafx.h"
#include "ComCounter.h"

int main() 
{
	try {
		HRESULT hr;
		CComPtr<ICounter> counter; // カウンタ

		// カウンタの生成
		hr = counter.CoCreateInstance(CLSID_counter);
		ComAssert(hr, "カウンタ生成失敗");

		// イベントハンドラの生成
		CComObject<CCounterEvents> *event; // カウンタイベントハンドラ
		CComObject<CCounterEvents>::CreateInstance(&event);

		// 接続
		hr = event->ConnectTo(counter);
		ComAssert(hr, "イベント接続失敗");

		// メソッド呼び出し
		int i;
		for (i = 0; i < 10; i++) counter->inc();
		for (i = 0; i < 10; i++) counter->dec();

		// 解放
		hr = event->Disconnect();
		ComAssert(hr, "イベント開放失敗");

		// いったん停止
		::MessageBox(NULL, "hoge", "hgoe", MB_OK);

	} catch ( std::exception& ex ) {
		std::cout << ex.what() << std::endl;
		return -1;
	}

	return 0;
}


作成日: 2005年08月24日12時27日13秒
更新履歴
2008年07月22日 2008年02月08日 2008年02月06日 2008年02月05日 2007年10月25日 2006年12月20日 2006年12月19日 2006年12月05日 2006年09月20日 2006年06月18日 2006年06月12日 2006年04月22日 2006年03月30日 2006年02月28日 2006年02月23日 2006年02月18日 2006年01月13日 2005年12月21日