桜、抹茶、白、日記

名古屋市在住のC++使いのcoderの日記だったもの。

コネクション ポイントの実装

ATLを使ってCOMのDLLを作っている訳ですが、コネクション ポイント(接続ポイント)の実装で大嵌り。

コネクション ポイントの実装
http://msdn.microsoft.com/ja-jp/library/s7zd1wd1.aspx

接続ポイントって何ぞって話なんですが、簡単に言えばイベントを実装する為の機能です。人によっては明日の出来事かも知れませんが。

Dr.GUI と COM イベント
http://msdn.microsoft.com/ja-jp/library/cc482696.aspx
http://msdn.microsoft.com/ja-jp/library/cc482700.aspx

C++のCOMとC#のクライアントという構成のサンプルがなかなか無いなと思ったらありました。

Microsoft All-In-One Code Framework
http://1code.codeplex.com/releases/view/57459

このCOMサンプルでATLCOMDllServerとC#クライアントで動作が確認出来ます。


ちょっと気になった事。

    1. ATLプロジェクトを新規作成
    2. プロジェクトを右クリックして追加でATLシンプルオブジェクト(例:CAtlSimpleObj1)を追加
    3. ウィザードのサポートオプションで、「ISupportErrorInfo」「接続ポイント」のチェックを入れる


CProxy_IXXXEvents の実装はウィザードで自動的にやってくれるみたい?

template <class T>
class CProxy_IAtlSimpleObj1Events
 : public IConnectionPointImpl<T, &__uuidof( _IAtlSimpleObj1Events ), CComDynamicUnkArray>
{
	// 警告: このクラスはウィザードで再生成されることがあります
public:
};

オブジェクトへのコネクション ポイントの追加
http://msdn.microsoft.com/ja-jp/library/7bkz4x17.aspx

上記記事を見ながらやってみたんですが、うまくいきませんでしたorz
んでうまくいったのは、

    1. 必要なI/Fをウィザードで追加する
    2. I/Fに対応するイベント関数を手動でIDL定義に追加する
    3. CAtlSimpleObj1クラスを右クリックして接続ポイントの追加ウィザードを実行する
  [ uuid(439665BF-DB1C-4EA0-A01D-C95A30B7A059) ]
  dispinterface _IAtlSimpleObj1Events
  {
    properties:
    methods:
      // ↓ここを追加↓
      [id(1), helpstring("Method1_Event")]
      HRESULT Method1_Event(LONG arg1);
      [id(2), helpstring("Prop1_Event")]
      HRESULT Prop1_Event(BSTR* pVal, LONG arg2, LONG arg3);
      // ↑ここを追加↑
  };
  [ uuid(77A6D6AF-5317-4DCA-9833-F598B55BC553) ]
  coclass AtlSimpleObj1
  {
    [default] interface IAtlSimpleObj1;
    [default, source] dispinterface _IAtlSimpleObj1Events;
  };


するとこんな感じで CProxy_IXXXEvents の実装が自動生成されました。何故か既定クラスの定義もちょっと変更されていたりしますが気にしない方向で。

template<class T>
class CProxy_IAtlSimpleObj1Events
 : public ATL::IConnectionPointImpl<T, &__uuidof(_IAtlSimpleObj1Events)>
{
public:
  HRESULT Fire_Method1_Event(LONG arg1)
  {
    HRESULT hr = S_OK;
    T * pThis = static_cast<T *>(this);
    int cConnections = m_vec.GetSize();

    for (int iConnection = 0; iConnection < cConnections; iConnection++)
    {
      pThis->Lock();
      CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
      pThis->Unlock();

      IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);
      if (pConnection)
      {
        CComVariant avarParams[1];
        avarParams[0] = arg1;
        avarParams[0].vt = VT_I4;
        CComVariant varResult;

        DISPPARAMS params = { avarParams, NULL, 1, 0 };
        hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);
      }
    }
    return hr;
  }
  HRESULT Fire_Prop1_Event(BSTR * pVal, LONG arg2, LONG arg3)
  {
    HRESULT hr = S_OK;
    T * pThis = static_cast<T *>(this);
    int cConnections = m_vec.GetSize();

    for (int iConnection = 0; iConnection < cConnections; iConnection++)
    {
      pThis->Lock();
      CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
      pThis->Unlock();

      IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);
      if (pConnection)
      {
        CComVariant avarParams[3];
        avarParams[2].byref = pVal;
        avarParams[2].vt = VT_BSTR|VT_BYREF;
        avarParams[1] = arg2;
        avarParams[1].vt = VT_I4;
        avarParams[0] = arg3;
        avarParams[0].vt = VT_I4;
        CComVariant varResult;

        DISPPARAMS params = { avarParams, NULL, 3, 0 };
        hr = pConnection->Invoke(2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);
      }
    }
    return hr;
  }
};

これで.NET Frameworkからもイベントがハンドリングできるようになりました。

あとは、関連してコレも気になったのでちょっと調べないとな。

SAMPLE: ATLCPImplMT encapsulates ATL event firing across COM apartments
http://support.microsoft.com/kb/280512/en
http://support.microsoft.com/kb/280512/ja

IConnectionPointImplMT v1.02 A freeware IConnectionPoint implementation for ATL
http://www.naughter.com/gpscom.html

Discussion of Marshal.ReleaseComObject and its dangers
http://blogs.msdn.com/b/yvesdolc/archive/2004/04/17/115379.aspx
More on ReleaseComObject (and why we did not implement IDisposable on the classes contained in the RCW)
http://blogs.msdn.com/b/yvesdolc/archive/2004/07/21/190691.aspx
[.NET][COM] Marshal.ReleaseComObject の危険性について
http://shinichiaoyagi.blog25.fc2.com/blog-entry-180.html
[.NET][COM] 続:Marshal.ReleaseComObject の危険性について
http://shinichiaoyagi.blog25.fc2.com/blog-entry-184.html