Excel ファイルまたは Word 文書の IDispatch を OCX から取得する方法

概要

OLE コントロールは自身のコンテナの IDispatch を必要とすることがよくあります。多くの場合、IDispatch を取得するには、サーバーにある IOleClientSite など、即座にアクセスできるインターフェイスから QueryInterface() を使用します。ただし、Microsoft Excel など、一部のサーバーでは、このアプローチは失敗します。



IDispatch を取得するもう 1 つの方法は、GetActiveObject() API を使用して、ROT からサーバーの IDispatch を取得する方法です。ただし、この方法を使用するには、サーバーの CLSID または ProgID を取得する必要があります。さらに、サーバーの複数のインスタンスを互いに区別できない、あいまいな状況が発生することがあります。



この資料では、別のアプローチを使用して IDispatch を取得します。このアプローチは、複数のインスタンスを実行している場合でも、Microsoft Excel と Microsoft Word のどちらでも機能します。



以下に記載されている手順を使用して、コンテナの Document オブジェクトの IDispatch を取得するコントロールをビルドできます。

詳細

手順の例

  1. OffCntrDisp という名前で新しい MFC ActiveX ControlWizard アプリケーションを作成します。

  2. 以下のメンバ変数を COleControl 派生クラスに追加します。

          char m_szDocName[512];
    IDispatch *m_pDocDisp;
    : m_szDocName はコントロールを含んでいるドキュメントの名前を保持します。m_pDocDisp はそのドキュメントの IDispatch インターフェイスです。



  3. 以下のコードを COleControl 派生クラス定義の末尾に追加します。

          // Interface Maps.
    protected:
    // IoleObject.
    BEGIN_INTERFACE_PART(MyOleObject, IOleObject)
    INIT_INTERFACE_PART(COffCtlDispCtrl, MyOleObject)
    STDMETHOD(SetClientSite)(LPOLECLIENTSITE);
    STDMETHOD(GetClientSite)(LPOLECLIENTSITE*);
    STDMETHOD(SetHostNames)(LPCOLESTR, LPCOLESTR);
    STDMETHOD(Close)(DWORD);
    STDMETHOD(SetMoniker)(DWORD, LPMONIKER);
    STDMETHOD(GetMoniker)(DWORD, DWORD, LPMONIKER*);
    STDMETHOD(InitFromData)(LPDATAOBJECT, BOOL, DWORD);
    STDMETHOD(GetClipboardData)(DWORD, LPDATAOBJECT*);
    STDMETHOD(DoVerb)(LONG, LPMSG, LPOLECLIENTSITE, LONG, HWND,
    LPCRECT);
    STDMETHOD(EnumVerbs)(IEnumOLEVERB**);
    STDMETHOD(Update)();
    STDMETHOD(IsUpToDate)();
    STDMETHOD(GetUserClassID)(CLSID*);
    STDMETHOD(GetUserType)(DWORD, LPOLESTR*);
    STDMETHOD(SetExtent)(DWORD, LPSIZEL);
    STDMETHOD(GetExtent)(DWORD, LPSIZEL);
    STDMETHOD(Advise)(LPADVISESINK, LPDWORD);
    STDMETHOD(Unadvise)(DWORD);
    STDMETHOD(EnumAdvise)(LPENUMSTATDATA*);
    STDMETHOD(GetMiscStatus)(DWORD, LPDWORD);
    STDMETHOD(SetColorScheme)(LPLOGPALETTE);
    END_INTERFACE_PART(MyOleObject)

    DECLARE_INTERFACE_MAP();
    このコードは、COleControl の、IOleObject のデフォルトの実装を、ユーザーのカスタム MyOleObject でオーバーライドするために追加されます。



  4. COleControl 派生クラスのコンストラクタに、以下のコードを追加します。

          m_pDocDisp = NULL;
  5. OffCntrDispCtl.cpp の次の行のすぐ後にコードを追加します。

          IMPLEMENT_OLECTLTYPE(COffCntrDispCtrl, IDS_OFFCNTRDISP,

    _dwOffCntrDispOleMisc)
    上記のコードの後に次のコードを追加します。

          BEGIN_INTERFACE_MAP(COffCntrDispCtrl, COleControl)
    INTERFACE_PART(COffCntrDispCtrl, IID_IOleObject, MyOleObject)
    END_INTERFACE_MAP()
    このコードは、手順 3. での変更と共に、COleControl の IOleObject をオーバーライドします。



  6. 手順 5. で追加したコードのすぐ下に、以下のコードを追加します。

          static char buf[8192];
    static void DoMsg(char *msg) {
    ::MessageBox(NULL, msg, "Message", MB_SETFOREGROUND);
    }

    static void DoErr(char *msg, long err) {
    static char errBuf[8192];
    sprintf(errBuf, "%s, Error: %ld (%08lx)", msg, err,err);
    ::MessageBox(NULL, errBuf, "Error", MB_SETFOREGROUND);
    }
    これらのコードは、メッセージを表示するために後で使用する、便利なルーチンです。



  7. 以下のすべてのコードを OffCntrDispCtl.cpp ファイルの末尾に貼り付けます。

          STDMETHODIMP COffCntrDispCtrl::XMyOleObject::SetHostNames(LPCOLESTR
    pwApp, LPCOLESTR pwObj)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);

    // Convert OLESTR into ASCII string.

    WideCharToMultiByte(CP_ACP, 0, pwObj, -1, pThis->m_szDocName,
    512, NULL, NULL);

    // Get IDispatch.
    pThis->GetDocDispatch();

    // Test it out by getting the document name.
    pThis->TestDispatch();

    return S_OK;
    }

    STDMETHODIMP
    COffCntrDispCtrl::XMyOleObject::SetClientSite(LPOLECLIENTSITE
    pClientSite)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.SetClientSite(pClientSite);
    }

    STDMETHODIMP
    COffCntrDispCtrl::XMyOleObject::SetColorScheme(LPLOGPALETTE plp)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.SetColorScheme(plp);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetMiscStatus(DWORD
    dwAspect, DWORD* pdwStatus)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.GetMiscStatus(dwAspect, pdwStatus);
    }

    STDMETHODIMP
    COffCntrDispCtrl::XMyOleObject::EnumAdvise(LPENUMSTATDATA*
    ppenumAdvise)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.EnumAdvise(ppenumAdvise);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::Unadvise(DWORD
    dwConnection)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.Unadvise(dwConnection);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::Advise(LPADVISESINK
    pAdvSink, DWORD* pdwConnection)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.Advise(pAdvSink, pdwConnection);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetExtent(DWORD
    dwDrawAspect, LPSIZEL lpsizel)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.GetExtent(dwDrawAspect, lpsizel);
    }


    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::SetExtent(DWORD
    dwDrawAspect, LPSIZEL lpsizel)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.SetExtent(dwDrawAspect, lpsizel);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetUserType(DWORD
    dwFormOfType, LPOLESTR* ppszUserType)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);

    return pThis->m_xOleObject.GetUserType(dwFormOfType,
    ppszUserType);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetUserClassID(CLSID*
    pClsid)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.GetUserClassID(pClsid);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::IsUpToDate()
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);

    return pThis->m_xOleObject.IsUpToDate();
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::Update()
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.Update();
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::EnumVerbs(LPENUMOLEVERB*
    ppenumOleVerb)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);

    return pThis->m_xOleObject.EnumVerbs(ppenumOleVerb);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::DoVerb(LONG iVerb, LPMSG
    lpmsg, LPOLECLIENTSITE pActiveSite, LONG lindex, HWND hwndParent,
    LPCRECT lprcPosRect)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.DoVerb(iVerb, lpmsg, pActiveSite,
    lindex, hwndParent, lprcPosRect);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetClipboardData(DWORD
    dwReserved, LPDATAOBJECT *ppDataObject)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.GetClipboardData(dwReserved,
    ppDataObject);
    }

    STDMETHODIMP
    COffCntrDispCtrl::XMyOleObject::InitFromData(LPDATAOBJECT
    pDataObject, BOOL fCreation, DWORD dwReserved)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.InitFromData(pDataObject, fCreation,
    dwReserved);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::GetMoniker(DWORD
    dwAssign, DWORD dwWhichMoniker, LPMONIKER *ppmk)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.GetMoniker(dwAssign, dwWhichMoniker,
    ppmk);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::SetMoniker(DWORD
    dwWhichMoniker, LPMONIKER pmk)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.SetMoniker(dwWhichMoniker, pmk);
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::Close(DWORD
    dwSaveOption)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.Close(dwSaveOption);
    }

    STDMETHODIMP
    COffCntrDispCtrl::XMyOleObject::GetClientSite(LPOLECLIENTSITE*
    ppClientSite)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.GetClientSite(ppClientSite);

    }

    STDMETHODIMP_(ULONG) COffCntrDispCtrl::XMyOleObject::Release()
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.Release();
    }

    STDMETHODIMP_(ULONG) COffCntrDispCtrl::XMyOleObject::AddRef()
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.AddRef();
    }

    STDMETHODIMP COffCntrDispCtrl::XMyOleObject::QueryInterface(REFIID
    iid, LPVOID* ppvObj)
    {
    METHOD_MANAGE_STATE(COffCntrDispCtrl, MyOleObject)
    ASSERT_VALID(pThis);
    return pThis->m_xOleObject.QueryInterface(iid, ppvObj);
    }
    このコードは IOleObject の実装であり、多くの場合、呼び出しをデフォルトの COleControl の IOleObject の実装にデリゲートします。ただし、SetHostNames() は例外です。SetHostNames() をトラップし、コントロールの挿入先ドキュメント名を格納します。



    残念なことに、Microsoft PowerPoint はこのメソッドを呼び出さないので、この例は Microsoft PowerPoint では機能しません。ただし、Microsoft PowerPoint は単一インスタンスのサーバーなので、GetActiveObject() を使用して一意に IDispatch ポインタを取得できます。


  8. COleControl 派生クラスに以下のメンバ関数を追加します。

          void COffCntrDispCtrl::GetDocDispatch()
    {
    // No need, if we already have it.
    if(m_pDocDisp != NULL) return;

    // Get a BindCtx.
    IBindCtx *pbc;
    HRESULT hr = CreateBindCtx(0, &pbc);
    if(FAILED(hr)) {
    DoErr("CreateBindCtx()", hr);
    return;
    }

    // Get running-object table.
    IRunningObjectTable *prot;
    hr = pbc->GetRunningObjectTable(&prot);
    if(FAILED(hr)) {
    DoErr("GetRunningObjectTable()", hr);
    pbc->Release();
    return;
    }

    // Get enumeration interface.
    IEnumMoniker *pem;
    hr = prot->EnumRunning(&pem);
    if(FAILED(hr)) {
    DoErr("EnumRunning()", hr);
    prot->Release();
    pbc->Release();
    return;
    }

    // Start at the beginning.
    pem->Reset();

    // Churn through enumeration.
    ULONG fetched;
    IMoniker *pmon;
    int n = 0;
    while(pem->Next(1, &pmon, &fetched) == S_OK) {

    // Get DisplayName.
    LPOLESTR pName;
    pmon->GetDisplayName(pbc, NULL, &pName);
    // Convert it to ASCII.
    char szName[512];
    WideCharToMultiByte(CP_ACP, 0, pName, -1, szName, 512, NULL,
    NULL);

    // Compare it against the name we got in SetHostNames().
    if(!strcmp(szName, m_szDocName)) {

    DoMsg("Found document in ROT!");

    // Bind to this ROT entry.
    IDispatch *pDisp;
    hr = pmon->BindToObject(pbc, NULL, IID_IDispatch, (void
    **)&pDisp);
    if(!FAILED(hr)) {
    // Remember IDispatch.
    m_pDocDisp = pDisp;

    // Notice...
    sprintf(buf, "Document IDispatch = %08lx",
    m_pDocDisp);
    DoMsg(buf);
    }
    else {
    DoErr("BindToObject()", hr);
    }
    }

    // Release interfaces.
    pmon->Release();

    // Break out if we obtained the IDispatch successfully.
    if(m_pDocDisp != NULL) break;
    }

    // Release interfaces.
    pem->Release();
    prot->Release();
    pbc->Release();
    }

    void COffCntrDispCtrl::TestDispatch()
    {
    ASSERT(m_pDocDisp);

    COleDispatchDriver doc(m_pDocDisp);
    DISPID dispID = 0;
    unsigned short *ucPtr = L"Name";

    // Get DISPID for Name.
    HRESULT hr = m_pDocDisp->GetIDsOfNames(IID_NULL, &ucPtr, 1,
    LOCALE_USER_DEFAULT, &dispID);
    ASSERT(!FAILED(hr));


    // Get Name property.
    CString name;
    doc.GetProperty(dispID, VT_BSTR, &name);

    AfxMessageBox(
    CString("Document name is ") + name,
    MB_SETFOREGROUND
    );
    }
  9. コンパイルを行います。

Microsoft Excel 97 で、以下の手順に従ってコントロールをテストします。


  1. Excel 97 を起動します。

  2. [コントロール ツールボックス] ツール バーを表示します ([表示] メニューで、[ツール バー] をクリックします)。

  3. [コントロール ツールボックス] ツール バーの右端にあるハンマーとレンチのアイコンをクリックし、 OffCntrDisp という名前の新しいコントロールを選択します。

  4. シートに四角形を描画し、コントロールを挿入します。



    結果 : コントロールが表示され、その後間もなく、"Found document in ROT" というメッセージを示すメッセージ ボックスが表示されます。次に、もう 1 つのメッセージ ボックスが表示され、"Document IDispatch = 0043bf8c." に近いメッセージが表示されます。最後に、コントロールを挿入したドキュメントの名前を知らせるメッセージ ボックスが表示されます。

Microsoft Office Excel 2007 で、以下の手順に従ってコントロールをテストします。

  1. Excel 2007 を起動します。

  2. [開発] タブをクリックします。[開発] タブがリボンに表示されない場合は、次の手順を実行してタブを表示します。

    1. Microsoft Office ボタンをクリックし、[Excel のオプション] をクリックします。

    2. [基本設定] タブをクリックし、[[開発] タブをリボンに表示する] チェック ボックスをオンにします。

    3. [OK] をクリックします。

  3. [開発] タブの [コントロール] で、[挿入] をクリックします。

  4. [ActiveX コントロール] で、[コントロールの選択] をクリックします。

  5. [コントロールの選択] ダイアログ ボックスで、[OffCntrDisp] をクリックし、[OK] をクリックします。

  6. シートに四角形を描画し、コントロールを挿入します。

    結果 : コントロールが表示され、その後間もなく、"Found document in ROT" というメッセージが記載されたメッセージ ボックスが表示されます。次に、もう 1 つのメッセージ ボックスが表示され、"Document IDispatch = 0043bf8c." に近いメッセージが表示されます。最後に、コントロールを挿入したドキュメントの名前を知らせるメッセージ ボックスが表示されます。

プロパティ

文書番号:190985 - 最終更新日: 2007/06/14 - リビジョン: 1

フィードバック