- PR -

オートメーションで、Excelを操作する時の、BOOKを閉じる前に関して

1
投稿者投稿内容
ポン太
会議室デビュー日: 2006/12/08
投稿数: 3
投稿日時: 2007-07-19 21:51
マイクロソフトの解説を頼りに、
Excelを操作するアプリケーションを作成しました。
ある条件のとき、BOOKを閉じたくないので、
VS2003 のクラスの新規作成
「TypeライブラリからMFCクラスの作成」 から、
「Microsoft Excel10.0 Object Library」を選び、
AppEvents を作成して「CAppEvents」クラスを自動生成しました。
このクラスの「WorkbookBeforeClose」がBOOKを閉じる前の処理に
関するものだと思うのですが、使用方法がわかりません。

1.引数には何を指定するのでしょうか?
2.呼ばれたときに、独自の処理を実行できるのでしょうか?
(コールバック関数のように使えますか?)

ぜひ使い方を教えていただけないでしょうか?

#実際に作成されたメンバ関数です
class CAppEvents : public COleDispatchDriver
{
void WorkbookBeforeClose(LPDISPATCH Wb, BOOL * Cancel)
{
static BYTE parms[] = VTS_DISPATCH VTS_PBOOL ;
InvokeHelper(0x622, DISPATCH_METHOD, VT_EMPTY, NULL, parms, Wb, Cancel);
}
}
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-07-20 19:17
こんにちは。

引用:

ポン太さんの書き込み (2007-07-19 21:51) より:
マイクロソフトの解説を頼りに、
Excelを操作するアプリケーションを作成しました。
ある条件のとき、BOOKを閉じたくないので、
VS2003 のクラスの新規作成
「TypeライブラリからMFCクラスの作成」 から、
「Microsoft Excel10.0 Object Library」を選び、
AppEvents を作成して「CAppEvents」クラスを自動生成しました。
このクラスの「WorkbookBeforeClose」がBOOKを閉じる前の処理に
関するものだと思うのですが、使用方法がわかりません。

1.引数には何を指定するのでしょうか?
2.呼ばれたときに、独自の処理を実行できるのでしょうか?
(コールバック関数のように使えますか?)

ぜひ使い方を教えていただけないでしょうか?

#実際に作成されたメンバ関数です
class CAppEvents : public COleDispatchDriver
{
void WorkbookBeforeClose(LPDISPATCH Wb, BOOL * Cancel)
{
static BYTE parms[] = VTS_DISPATCH VTS_PBOOL ;
InvokeHelper(0x622, DISPATCH_METHOD, VT_EMPTY, NULL, parms, Wb, Cancel);
}
}



MFCではExcelのイベントを拾うのは無理なのかな…
途中断念しましたが、途中までのやり方を書いておきます。


まずAppEventsディスパッチインターフェイスはこちらから”呼び出す”ものではなく、
コネクタブルオブジェクトであるExcel.Application側から”呼び出される”ものです。
なので、AppEventsディスパッチインターフェイス(IDispatchインターフェイス)をこちらで実装してやる必要があります。
※COleDispatchDriverクラスはこちらから”呼び出す”ためのヘルパークラスです。

MFCでは通常CCmdTargetクラスでオートメーションのサポートがされていますので、
MFCクラスの追加で基本クラスにCCmdTargetを指定して、オートメーションをオンにして生成します。
コード:
class CAppEvents : public CCmdTarget
{




コネクタブルオブジェクトであるExcel.Applicationオブジェクトからイベントを受け取るために、
CAppEventsオブジェクトと接続してやります。

まずExcel.ApplicationオブジェクトからIConnectionPointContainerインターフェイスを取得し、
IConnectionPointContainer::FindConnectionPointでIConnectionPointインターフェイスを取得します。
あとは、IConnectionPoint::AdviseでCAppEventsオブジェクトに繋げてやります。

こんな感じ…
コード:

	// uuid(00024413-0000-0000-C000-000000000046) dispinterface AppEvents 
	IID IID_dispAppEvents = {0x00024413, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};

	CApplication excelApp;
	CAppEvents   events;

	VERIFY(excelApp.CreateDispatch("Excel.Application"));

	// IConnectionPointContainerインターフェイスの取得
	IConnectionPointContainer*	pConContainer;
	VERIFY(SUCCEEDED(excelApp.m_lpDispatch->QueryInterface(IID_IConnectionPointContainer, (void**)&pConContainer)));

	// AppEventsのIConnectionPointインターフェイスの取得
	IConnectionPoint*	pConPoint;
	VERIFY(SUCCEEDED(pConContainer->FindConnectionPoint(IID_dispAppEvents, &pConPoint)));
	pConContainer->Release();

	// 接続
	DWORD	dwCookie;
	pConPoint->Advise(events.GetIDispatch(FALSE) , &dwCookie);



これでコールバックが受け取れるはずなのですが、

ExcelはIDispatch::GetIDsOfNamesを呼び出さず、直接IDispatch::Invokeでコールバックしてくるようです(行儀が悪いなぁ)。
なのでCAppEventsクラスにはちゃんと決められたディスパッチIDとメソッドを関連付けておく必要があります。。
ディスパッチIDを明示的に指定するにはDISP_FUNCTION_IDマクロを使えば出来るようです。

※ここからが問題※
また、Excelは「名前付き引数」でコールバックしてきます。
どうもCCmdTargetクラスでは名前付き引数をサポートしておらず、DISP_E_NONAMEDARGSエラーを返してしまうようです。


あとは、誰か続きをお願いしまぁす^^;
Atata!!
常連さん
会議室デビュー日: 2007/05/22
投稿数: 20
投稿日時: 2007-07-21 02:18
以下の接続ポイント作成の参考情報を踏まえて。
http://support.microsoft.com/kb/309309/


引用:

ExcelはIDispatch::GetIDsOfNamesを呼び出さず、直接IDispatch::Invokeでコールバックしてくるようです(行儀が悪いなぁ)。


これは仕様通りです。
接続ポイントに対してAppEventsのIIDを要求しているため、
イベントインターフェースはAppEventsを実装しているとみなされます。
つまり、DISPIDは自明なものとして処理されます。


引用:

また、Excelは「名前付き引数」でコールバックしてきます。
どうもCCmdTargetクラスでは名前付き引数をサポートしておらず、DISP_E_NONAMEDARGSエラーを返してしまうようです。


私の知る限りMFCで名前付き引数を処理する方法はありません。
# Visual Studio 2005 は未調査
CCmdTargetクラスに処理される前段階のCOleDispatchImplクラスでDISP_E_NONAMEDARGSを返却しているようです。

Excelに限って言えばIDispatchを実装したクラスを作成した方が早いと私は考えています。
必須となるメソッドは以下の4つだけですので、それほど手間は掛からないと思います。
・IUnknown::QueryInterface
・IUnknown::AddRef
・IUnknown::Release
・IDispatch::Invoke
# 基礎知識が無いなら話は別ですが・・・。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-07-21 14:04
こんにちは。

フォローどうもありがとうございます。

引用:

Atata!!さんの書き込み (2007-07-21 02:18) より:
引用:

ExcelはIDispatch::GetIDsOfNamesを呼び出さず、直接IDispatch::Invokeでコールバックしてくるようです(行儀が悪いなぁ)。


これは仕様通りです。
接続ポイントに対してAppEventsのIIDを要求しているため、
イベントインターフェースはAppEventsを実装しているとみなされます。
つまり、DISPIDは自明なものとして処理されます。


なるほど。”行儀が悪い”の発言は、私の主観的意見(単にDISPIDを合わせるのが面倒だったので)だったようです。

引用:

私の知る限りMFCで名前付き引数を処理する方法はありません。
# Visual Studio 2005 は未調査
CCmdTargetクラスに処理される前段階のCOleDispatchImplクラスでDISP_E_NONAMEDARGSを返却しているようです。


やはりMFCでは名前付き引数が処理できないっぽいですね。
#実はここ数日の断続的な頭痛に悩まされながら、昨日この辺りでハマって調査していたのですが、
 結局あきらめてしまいました。

引用:

Excelに限って言えばIDispatchを実装したクラスを作成した方が早いと私は考えています。
必須となるメソッドは以下の4つだけですので、それほど手間は掛からないと思います。
・IUnknown::QueryInterface
・IUnknown::AddRef
・IUnknown::Release
・IDispatch::Invoke
# 基礎知識が無いなら話は別ですが・・・。


同意です。
目的のイベント(WorkbookBeforeClose)を拾うだけなら、IDispatch::Invokeの実装も手を抜けそうですし…
自分で作ってしまうのが良い気がします。
ポン太
会議室デビュー日: 2006/12/08
投稿数: 3
投稿日時: 2007-07-22 15:11
Tdnr_Sym様、Atata!!様返信ありがとうございます。
MFCで自動作成したクラスで(楽に)実装できるものだと思っていたので、
少し、残念ですが、Atata!!様の助言どうり、
IDispatchを実装したクラスを作成したいと思います!


引用:
--------------------------------------------------------------------------------


Excelに限って言えばIDispatchを実装したクラスを作成した方が早いと私は考えています。
必須となるメソッドは以下の4つだけですので、それほど手間は掛からないと思います。
・IUnknown::QueryInterface
・IUnknown::AddRef
・IUnknown::Release
・IDispatch::Invoke
# 基礎知識が無いなら話は別ですが・・・。

--------------------------------------------------------------------------------

Atata!!
常連さん
会議室デビュー日: 2007/05/22
投稿数: 20
投稿日時: 2007-07-22 21:17
引用:

Excelに限って言えばIDispatchを実装したクラスを作成した方が早いと私は考えています。


独自に実装されるのならば、以下のような手法もあります。

Excelのイベントインターフェースには純粋ディスパッチインターフェース(AppEvents)と
デュアルインターフェース(IAppEvents)の2つの定義があります。
デュアルインターフェース側の型情報(ITypeInfo)をタイプライブラリから取得しておいて、
イベントを実装したクラスのIDispatch::Invoke内で
取得しておいたITypeInfo::Invokeメソッドを呼び出すことにより
デュアルインターフェースの個別のメソッドに呼び出しを転送するという手法です。
ITypeInfo::Invokeは正確に名前付き引数を処理するので、
名前付き引数を自力で解析する必要がなくなります。

この手法の難点として・・・
・イベントを処理するクラスでIAppEventsの全てのメソッドを実装する必要があること
・読み込むタイプライブラリを正確に把握している必要があること
等が挙げられます。
# 自分でIDLを書けば、上記の問題をある程度軽減することもできますが・・・。

以上、ご参考まで。
MFCが名前付き引数を処理できないなら、システムに処理させる技とでも言いましょうか。


なお、上記以外にATLでも条件付きながら名前付き引数を処理することができます。
1

スキルアップ/キャリアアップ(JOB@IT)