VCでActiveXコンポーネント(COMも含む)のイベントを扱う
VCでCOMのイベントを受信するには,
イベントシンクを実装する必要があります.
サンプルを作ってみたので,以下にソースを載せます.
このサンプル作成に当たっては,以下のサイトを参考にしました.
- 参考サイト:
- http://www.s34.co.jp/cpptechdoc/misc/comevent/ (COMからのイベントを捕まえる方法)
- http://www.angel.ne.jp/~shibu/dk_addin/vc6.html (VC6で作るdkアドイン )
.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秒
![[トップページ]](../lib/img//top.gif)
![[一覧]](../lib/img//list.gif)
![[検索]](../lib/img//search.gif)
![[ヘルプ]](../lib/img//help.gif)
![[ログイン]](../lib/img//login.gif)