Create  Edit  Diff  FrontPage  Index  Search  Changes  Login

The Backyard - ComWrapper Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

!目的
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 長さパラメータ付きメソッドを追加