LoginSignup
3
3

More than 5 years have passed since last update.

Visual Studio 2017 Visual C++ による IDispatch インターフェースの使用例

Posted at

はじめに

COM の要件は IUnknown インターフェースを実装していることです。しかし、要件ではないですが、IDispatch インターフェースを実装するのが一般的です。

これは何かといえば、Java や C# の「リフレクション」のような機能を実現するためのインターフェースです。IDispatch が実装されていると、Invoke メソッドを使って COM オブジェクトのプロパティやメソッドを利用できます。

IDispatch の使用手順

  • TLB をインポートしてオブジェクトの諸元を得る。
  • COM を初期化して使用可能にする。
  • COM オブジェクトを作成する。
  • IDispatch を実装しているか調べる。
  • Invoke メソッドに渡すためのパラメータを作成する。
  • Invoke メソッドを呼び出す。

サンプル

前述の手順をコードとして記述したサンプルを示します。

実はこのサンプルですが、完全ではありません。Invoke メソッドのパラメータとして SAFEARRAY を渡す必要があるのですが、それの作り方がよくわからず途中で頓挫してしまいました。

SAFEARRAY を作るための API 関数を使うといくらか楽になるようです。(SafeArrayCreate)

// CheckDualInterface.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#import "C:\lib\AxStrFuncs.tlb" named_guids no_namespace

void CreateParams(VARIANTARG *sarr, LPCTSTR str, short n, BSTR ret);

//
//  IDispatch を実装した COM オブジェクトの使用法
//  ==============================================
int main()
{
    IUnknown *punk;
    IDispatch *pdisp;
    DISPID dispid;
    OLECHAR *pName;  // OLECHAR は WCHAR の別名
    VARIANTARG arg1[3];
    DISPPARAMS dispparams;

    // 開始メッセージ表示
    printf_s("Check IDispatch installed.\n");

    // COM 初期化
    HRESULT hr = OleInitialize(NULL);  // 内部的に ComInitializeEx を呼び出している。
    if (FAILED(hr))
    {
        printf_s("Failed to initialize COM.\n");
        return -1;
    }

    // オブジェクトを作成する。
    hr = CoCreateInstance(__uuidof(StrFuncObject), NULL, CLSCTX_SERVER, IID_IUnknown, (void**)&punk);
    if (FAILED(hr))
    {
        printf_s("Failed to create the object.\n");
        return -1;
    }

    // IDispatch を実装しているか調べる。
    hr = punk->QueryInterface(IID_IDispatch, (void**)&pdisp);
    if (FAILED(hr))
    {
        printf_s("IDispatch is not suppored.\n");
        return -1;
    }

    // オブジェクトのメソッド Right の DISPID を得る。
    CComBSTR right(L"Right");
    pName = right;
    hr = pdisp->GetIDsOfNames(IID_NULL, &pName, 1, LOCALE_USER_DEFAULT, &dispid);
    if (FAILED(hr))
    {
        printf_s("IDispatch is not suppored.\n");
        return -1;
    }
    else
    {
        printf_s("Right Disp ID = %d\n", dispid);
    }

    // Right メソッドのパラメータを作成する。
    BSTR bstr = SysAllocString(L"");
    CreateParams(arg1, L"ABCDEFGH", 3, bstr);
    // SAFEARRAY でないと NG
    // dispparams.rgvarg->parray = arg1;
    dispparams.rgvarg->vt = VT_ARRAY;
    dispparams.cArgs = 2;
    dispparams.cNamedArgs = NULL;
    dispparams.rgdispidNamedArgs = NULL;
    VARIANT vResult;  // 戻り値
    vResult.vt = VT_BSTR | VT_BYREF;
    vResult.pbstrVal = &bstr;

    // Invoke メソッドを使って COM オブジェクトの Right メソッドを実行する。
    hr = pdisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparams, &vResult, NULL, NULL);
    if (FAILED(hr))
    {
        printf_s("Invoke method has failed.\n");
        return -1;
    }
    else
    {
        wprintf_s(L"%s\n", bstr);
    }

    // 終わり
    printf_s("Done.\n");

    SysFreeString(bstr);
    getchar();
    return 0;
}

//
//  パラメータを作成する。
//  =====================
void CreateParams(VARIANTARG *sarr, LPCTSTR str, short n, BSTR ret)
{
    VARIANT *p1, *p2, *p3;
    p1 = sarr;
    p1->vt = VT_BSTR;
    p1->bstrVal = CComBSTR(str);
    p2 = sarr + 1;
    p2->vt = VT_I2;
    p2->iVal = n;
    p3 = sarr + 2;
    p3->vt = VT_BSTR;
    p3->bstrVal = ret;
}
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3