challenge メソッド名一覧の表示

リフレクション系のお題の続編です。

「ある与えられたオブジェクトtargetのメソッドのうち、 "test_"で始まるものをすべて呼びだす」というコードを書いてください。 引数に関しては都合のいいように仮定して構いません(全部0個、など)。

メソッドという概念がない言語の場合は、 「複数の関数への参照を持っているようなオブジェクト(たとえばパッケージとかモジュールとか)から"test_"で始まる関数をすべて呼び出す」と読み替えても構いません。

Posted feedbacks - C++

ここでまさかのC++。ええと、WindowsでCOM使っています。VBやスクリプト言語用のIDispatch/ITypeInfoでリフレクションに相当する情報が得られるので、そこからメソッド名test_で始まるものを選び、IDipatchのInvokeメソッドで呼び出しています。

main関数より後ろはヘルパ群、ITypeInfoの型情報やIDispatch対応オブジェクトの作成などといった関数が並んでいます。

ITypeInfoはIDLから作るしかないと思っていたところ、このプログラムのようにCreateDispTypeInfo関数を使うとC++ソースコードの記述だけでITypeInfoを作れることを知りました。これを知らなければこの課題をC++でやることはなかったでしょう。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//#define WIN32_LEAN_AND_MEAN

#include <iostream>

#include <ole2.h>
#include <windows.h>
#include <comdef.h>

#include <boost/implicit_cast.hpp>
using boost::implicit_cast;

// ここでは再発明しましたが、COMSTL (WinSTLの兄弟)のcom_exceptionがお薦め
class ComException
{
public:
    explicit ComException(HRESULT hr) : hr(hr) {}
    ComException(const ComException& rhs) : hr(rhs.hr) {}
    ComException& operator =(const ComException& rhs)
    {
        hr = rhs.hr;
        return *this;
    }
    HRESULT GetErrorCode() const
    {
        return hr;
    }
    //swapは使わないので省略
public:
    HRESULT hr;
};

void ThrowIfFailed(HRESULT hr)
{
    if (FAILED(hr))
    {
        throw ComException(hr);
    }
}

// COM関係のヘッダに#define interface structとある。
interface __declspec(uuid("c70cd2de-1285-4ec5-a78a-5600cf6fc79a")) __declspec(novtable)
ITest : IUnknown
{
    STDMETHOD(test_Hello)() PURE;
    STDMETHOD(test_Goodbye)() PURE;
    STDMETHOD(piyo_Dummy)() PURE;
    // マクロを全て展開するとこうなる。
    // virtual __declspec(nothrow) HRESULT __stdcall test_Hello() = 0;
};

// ITestの実装例
class TestImpl : public ITest
{
public:
    TestImpl() : refCount(1) {}

    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv)
    {
        if (riid == __uuidof (IUnknown))
        {
            *ppv = implicit_cast<IUnknown*>(this);
        }
        else if (riid == __uuidof (ITest))
        {
            *ppv = implicit_cast<ITest*>(this);
        }
        else
        {
            *ppv = 0;
            return E_NOTIMPL;
        }
        return S_OK;
    }

    ULONG STDMETHODCALLTYPE AddRef()
    {
        return ++refCount;
    }

    ULONG STDMETHODCALLTYPE Release()
    {
        if (--refCount)
        {
            delete this;
            return 0;
        }
        return refCount;
    }

    HRESULT STDMETHODCALLTYPE test_Hello()
    {
        std::cout << "Hello, world!" << std::endl;
        return S_OK;
    }
    HRESULT STDMETHODCALLTYPE test_Goodbye()
    {
        std::cout << "Goodbye!" << std::endl;
        return S_OK;
    }
    HRESULT STDMETHODCALLTYPE piyo_Dummy()
    {
        return E_NOTIMPL;
    }
private:
    ULONG refCount;
};

// もし、IホゲPtrが無くてエラーになるなら。
//_COM_SMARTPTR_TYPEDEF(ITypeInfo, __uuidof (ITypeInfo));
//_COM_SMARTPTR_TYPEDEF(IUnknown, __uuidof (IUnknown));
//_COM_SMARTPTR_TYPEDEF(IDispatch, __uuidof (IDispatch));

ITypeInfoPtr GetFirstInterfaceTypeInfo(ITypeInfo* pti);
IDispatchPtr CreateDispTest(ITypeInfo* pti);
ITypeInfoPtr GetTestTypeInfo();

int main()
{
    try
    {
        ThrowIfFailed(OleInitialize(0));
        ITypeInfoPtr pti = GetTestTypeInfo(); //クラスを表すITypeInfo
        IDispatchPtr target = CreateDispTest(pti); //オブジェクトの作成
        ITypeInfoPtr ptiInterface = GetFirstInterfaceTypeInfo(pti); //ITestを表すITypeInfo
        TYPEATTR* pta;
        ThrowIfFailed(ptiInterface->GetTypeAttr(&pta));
        //DISPPARAMS dp2 = {};
        //ThrowIfFailed(pdisp->Invoke(2, IID_NULL, LOCALE_SYSTEM_DEFAULT,
        //    DISPATCH_METHOD, &dp2, 0, 0, 0));
        for (int i = 0; i < pta->cFuncs; ++i)
        {
            // i個目の関数のMEMIDを取得
            FUNCDESC* pfd;
            ThrowIfFailed(ptiInterface->GetFuncDesc(i, &pfd));
            // その関数の名前を取得
            _bstr_t name;
            UINT count;
            ThrowIfFailed(ptiInterface->GetNames(pfd->memid, name.GetAddress(), 1, &count));
            if (wmemcmp(name, L"test_", 5) == 0)
            {
                 // その関数がtest_で始まっていれば引数なしで呼び出す
                DISPPARAMS dp = {};
                ThrowIfFailed(target->Invoke(pfd->memid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
                    DISPATCH_METHOD, &dp, 0, 0, 0));
            }
            ptiInterface->ReleaseFuncDesc(pfd);
        }
        ptiInterface->ReleaseTypeAttr(pta);
    }
    catch(ComException const& e)
    {
        LPSTR msg;
        FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
            NULL, e.GetErrorCode(), LANG_USER_DEFAULT,
            reinterpret_cast<LPSTR>(&msg), 0, 0);
        std::cerr << msg << std::endl;
        LocalFree(msg);
    }
    OleUninitialize();
}

// CreateDispTypeInfoを使ったお手軽ITypeInfoの作成。
ITypeInfoPtr GetTestTypeInfo()
{
    static METHODDATA md[] =
    {
        {
            /* szName = */ L"test_Hello",
            /* ppdata = */ 0, //引数情報
            /* dispid = */ 1, //IDispatch::Invokeで呼び出すときの識別番号。適当な正の値で良い。
            /* iMeth = */ 3, //VTBL上のインデックス
            /* cc = */ CC_STDCALL, //呼出規約
            /* cArgs = */ 0, //引数の個数
            /* wFlags = */ DISPATCH_METHOD, //メソッド・プロパティの種別
            /* vtReturn = */ VT_EMPTY, //戻り値の型
        },
        {
            /* szName = */ L"test_Goodbye",
            /* ppdata = */ 0,
            /* dispid = */ 2,
            /* iMeth = */ 4,
            /* cc = */ CC_STDCALL,
            /* cArgs = */ 0,
            /* wFlags = */ DISPATCH_METHOD,
            /* vtReturn = */ VT_EMPTY,
        },
        {
            /* szName = */ L"piyo_Dummy",
            /* ppdata = */ 0,
            /* dispid = */ 3,
            /* iMeth = */ 5,
            /* cc = */ CC_STDCALL,
            /* cArgs = */ 0,
            /* wFlags = */ DISPATCH_METHOD,
            /* vtReturn = */ VT_EMPTY,
        },
    };
    static INTERFACEDATA id =
    {
        md, ARRAYSIZE(md), // ARRAYSIZEは<windows.h>の中で定義されている配列の要素数を求めるマクロ
    };
    ITypeInfoPtr pti;
    ThrowIfFailed(CreateDispTypeInfo(&id, LOCALE_SYSTEM_DEFAULT, &pti));
    return pti;
}

// TestImplのディスパッチオブジェクトを作成。
// ただし、ITypeInfを引数として与えること。
IDispatchPtr CreateDispTest(ITypeInfo* pti)
{
    TestImpl* p = new TestImpl;
    IUnknown* punkDispObj;
    ThrowIfFailed(CreateStdDispatch(p, p, pti, &punkDispObj));
    return punkDispObj;
}

// あるITypeInfoから、1番目に継承もしくは実装しているインタフェースを返すヘルパ。
ITypeInfoPtr GetFirstInterfaceTypeInfo(ITypeInfo* pti)
{
    ITypeInfoPtr res;
    HREFTYPE hrt;
    ThrowIfFailed(pti->GetRefTypeOfImplType(0, &hrt));
    ThrowIfFailed(pti->GetRefTypeInfo(hrt, &res));
    return res;
}

Index

Feed

Other

Link

Pathtraq

loading...