Create  Edit  Diff  FrontPage  Index  Search  Changes  Login

ComWrapper

目的

C++のクラスFooをCOMのインターフェイスを使って異なるオブジェクト間でやりとりしたい。

注意

IDLの書式やマクロ、APIがそのままコンパイル可能かどうかは未検証(多分、間違いを含む)

方法

Fooのラッパを作る。(以下、C++を前提とする)

元のクラス

class Foo
{
public:
    void doFoo();
    char* getChars();
    void setChars(char* p);
}

IDL

[
 object,
 uuid( /** GUIDGENで生成したGUIDを埋める **/ ),
 pointer_default(unique)
]
interface IFoo : IUnknown
{
    HRESULT doFoo();
    HRESULT getChars([out, string]char** ppResult); /* outの長さ属性にstringが書けるかちょっと自信ない。*/
    HRESULT getCharsWithLength([out]short* length, [out, size_is(,*length)] char** ppResult); /* 別解 バイナリデータ用 */
    HRESULT setChars([in, string]char* chars);
};

実装

APIはうろ覚えなので、間違えている可能性がある。

#include "idlgen.h" // IDLが生成したヘッダのIFooを参照

public class FooWrapper : IFoo
{
    Foo* foo;
    int refCount;
    FooWrapper(Foo* initFoo)
    {
        foo = initFoo;
        refCount = 1;  // 生成されたら1
    }
    ~FooWrapper() // 常識的にはvirtual宣言すべきだが、派生は考えないのでこれで良いことにする
    {
        delete foo;
    }
    HRESULT STDCALLTYPE QueryInterface(const IID& riid, void** ppv)
    {
        if (IsEqualIID(riid, GUID_IUnknown) || IsEqualIID(riid, GUID_IFoo))
        {
            refCount++;
            *ppv = this;
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    ULONG AddRef()
    {
        return ++refCount;
    }
    ULONG Release()
    {
        ULONG ret = --refCount;
        if (!ret)
        {
            delete this;
        }
        return ret;
    }
    HRESULT doFoo()
    {
        try {
            foo->doFoo();
        } catch {               // C++の例外の書き方忘れた
            return E_EXCEPTION;
        }
        return S_OK;
    }
    HRESULT getChars(unsigned char** out) // 確かmidlがcharをunsigned charに変換すると記憶している
    {
        if (!out) return E_POINTER;  // ヒープかどうかのチェックもすべき
        char* result =foo->getChars();
        int size = strlen(result);
        *out = CoTaskMemAlloc(size + 1);
        if (!*out) return E_NOMEMORY;
        strcpy(reinterpret_cast<char*>(*out), result);
        return S_OK;
    }
    HRESULT getCharsWithLength(short* length, unsigned char** out)
    {
        if (!length || !out) return E_POINTER;
        char* result =foo->getChars();
        *length = reinterpret_cast<short>(strlen(result));
        *out = CoTaskMemAlloc(*length);
        if (!*out) return E_NOMEMORY;
        memcpy(*out, result, *length);
        return S_OK;
    }
    HRESULT setChars(unsigned char* p)
    {
        foo->setChars(reinterpret_cast<char*>(p));
        return S_OK;
    }
}

返す側

HRESULT getFoo(IFoo** pp)
{
    if (pp == NULL) return E_POINTER;
    *pp = new FooWrapper(new Foo());    // refcount == 1
    return S_OK;
}

使う側

IFoo* p;
if (Supplier->getFoo(&p) == S_OK)
{
    p->doFoo();
    ...
    unsigned char* mem = p->getChars();
    ...
    CoTaskMemFree(mem);
    ...
    p->Release();    // refcount == 0 -> deleteされる
 }

更新履歴

  • 2007/02/02 midlが生成するヘッダではcharがunsigned charになる(と思った)を反映
  • 2007/02/02 メモリーアロケーションのバグを修正
  • 2007/02/02 長さパラメータ付きメソッドを追加
Last modified:2007/02/03 14:36:07
Keyword(s):
References: