継承の方法
このページはかなり昔に書いた資料です。今 COM コンポーネントを作るとしたらこの方法では行わないでしょうが、 基本的な動作を理解するためには役に立つかもしれないと思い、ここに掲載しています。
このページの目標
ここでは COM における継承について記述します。 "包含" と呼ばれているものです。 また、簡単な実装例も示します。
参照: Don Box et al, Effective COM の、28 節 『COM 集約と COM 包含はアイデンティティ操作のトリックであり、コードの再利用のテクニックではない。』 も一読しましょう。
概要
Fig 1. 継承
今、犬 (Dog) のプログラムを作りたいとします。 もし、既に動物 (Animal) の一般的な動作を定義しているコンポーネントが存在しているのならば、それを拡張して犬のプログラムが作れたら便利でしょう。 つまり、呼吸をする、ご飯を食べる、寝るなどの動作は一般的にすべての動物に当てはまるので、それを "動物コンポーネント" にまとめておいて、犬の動作 (例えば、「ワンワン」と吠えるなど) を表現したい場合に、"動物全般の動作 + 犬特有の動作" という形で犬を表現するのです。 こういう場合、「犬は動物の動作を継承 (inheritance) している」といいます。
さて、COM では継承はどのように実装すればよいでしょうか。 左図をご覧ください。 内側にコンポーネントを包含するコンポーネントが描かれています。 図では、犬 (Dog) コンポーネントが動物 (Animal) コンポーネントを包含しています。このコンポーネントは犬コンポーネントであり、インターフェイスは IDog と名づけられています。 IDog の機能は "動物全般の動作 + 犬特有の動作" であり、動物全般の動作が必要な場合は、内部の動物コンポーネントを使用します。一方、犬特有の動作が必要な場合は、ここで新たにそれを定義すればよいのです。
このように、既存のコンポーネントを再利用することにより、開発の効率を上げることが出来ます。
実装の方法としては、外側コンポーネントのメンバーとして、内側コンポーネントインターフェイスへのポインタを定義し、それを外側の Initialize 時に設定します。
実装例
Client.cpp
#include <iostream.h> #include <objbase.h> #include "Dog.h" int main() { HRESULT hr; hr = CoInitialize(NULL); if(FAILED(hr)) { return 1; } IDog* pIDog = NULL; hr = CoCreateInstance(CLSID_DOG, NULL, CLSCTX_INPROC_SERVER, IID_IDOG, (void**)&pIDog); if(SUCCEEDED(hr)) { pIDog->Speak(); pIDog->Eat(); pIDog->Play(); pIDog->Release(); } else { cout << "ERROR: CoCreateInstance()" << endl; } CoUninitialize(); return 0; }Animal.cpp
NOTE: レジストリへの自己登録関連ヘルパ関数の実装は省略しています。 VC++ による単純な COM コンポーネントの作成 と全く同一です。
// Animal.cpp #include <objbase.h> #include <iostream.h> #include <assert.h> #include "Animal.h" //global variables static HINSTANCE g_hModule = NULL; // DLL module handler static long g_cComponents = 0; // Count of active components static long g_cServerLocks = 0; // Count of Locks const char g_szFriendlyName[] = "Test Compont, Copyright(C)1999"; const char g_szVerIndProgID[] = "OyamaCom.Animal"; const char g_szProgID[] = "OyamaCom.Animal.1"; //***** Exported Functions ***** BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, void* lpReserved) { if(dwReason == DLL_PROCESS_ATTACH) { g_hModule = hModule; } return TRUE; } STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) { if(clsid != CLSID_ANIMAL) { return CLASS_E_CLASSNOTAVAILABLE; } CAnimalFactory* pFactory = new CAnimalFactory; if(pFactory == NULL) { return E_OUTOFMEMORY; } //Get requested interface HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release(); return hr; } STDAPI DllCanUnloadNow() { if((g_cComponents==0) && (g_cServerLocks==0)) { return S_OK; } else { return S_FALSE; } } STDAPI DllRegisterServer() { return RegisterServer(g_hModule, CLSID_ANIMAL, g_szFriendlyName, g_szVerIndProgID, g_szProgID); } STDAPI DllUnregisterServer() { return UnregisterServer(CLSID_ANIMAL, g_szVerIndProgID, g_szProgID); } //***** Class Factory Object ***** // *** IUnknown methods *** HRESULT __stdcall CAnimalFactory::QueryInterface(const IID& riid, void** ppvObj) { if((riid==IID_IUnknown) || (riid==IID_IClassFactory)) { *ppvObj = static_cast<IClassFactory*>(this); } else { *ppvObj = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppvObj)->AddRef(); return S_OK; } ULONG __stdcall CAnimalFactory::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG __stdcall CAnimalFactory::Release() { return InterlockedDecrement(&m_cRef); } // *** IClassFactory methods *** HRESULT __stdcall CAnimalFactory::CreateInstance(IUnknown* pUnkOuter, const IID& riid, void** ppvObj) { //Cannot Aggregate if(pUnkOuter != NULL){ return CLASS_E_NOAGGREGATION; } //Create Component CAnimal* pAnimal = new CAnimal; if(pAnimal == NULL) { return E_OUTOFMEMORY; } //Get the requested interface HRESULT hr = pAnimal->QueryInterface(riid, ppvObj); //Release the IUnknown pointer. pAnimal->Release(); return hr; } HRESULT __stdcall CAnimalFactory::LockServer(BOOL bLock) { if(bLock) { InterlockedIncrement(&g_cServerLocks); } else { InterlockedDecrement(&g_cServerLocks); } return S_OK; } CAnimalFactory::CAnimalFactory() : m_cRef(1) { } CAnimalFactory::~CAnimalFactory() { } //***** CAnimal ***** // *** IUnknown methods *** HRESULT __stdcall CAnimal::QueryInterface(const IID& riid, void** ppvObj) { if(riid == IID_IUnknown) { *ppvObj = static_cast<IAnimal*>(this); } else if(riid == IID_IANIMAL) { *ppvObj = static_cast<IAnimal*>(this); } else { *ppvObj = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppvObj)->AddRef(); return S_OK; } ULONG __stdcall CAnimal::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG __stdcall CAnimal::Release() { return InterlockedDecrement(&m_cRef); } // IAnimal HRESULT __stdcall CAnimal::Eat() { cout << "Animals eat foods." << endl; return S_OK; } HRESULT __stdcall CAnimal::Speak() { cout << "Animals speak something." << endl; return S_OK; } HRESULT __stdcall CAnimal::Play() { cout << "Animals are enjoying." << endl; return S_OK; } //Constructor CAnimal::CAnimal() : m_cRef(1) { InterlockedIncrement(&g_cComponents); } //Destructor CAnimal::~CAnimal() { InterlockedDecrement(&g_cComponents); }Animal.h
#include "RegisterRelated.h" #if !defined(__ANIMAL__) #define __ANIMAL__ // {12058E90-A8A4-4650-9FFB-04495149820A} const GUID CLSID_ANIMAL = { 0x12058e90, 0xa8a4, 0x4650, { 0x9f, 0xfb, 0x4, 0x49, 0x51, 0x49, 0x82, 0xa } }; //***** Difinition of Interfaces ***** // {9967F54F-9DE0-43de-ADF9-97ADD45C464C} static const GUID IID_IANIMAL = { 0x9967f54f, 0x9de0, 0x43de, { 0xad, 0xf9, 0x97, 0xad, 0xd4, 0x5c, 0x46, 0x4c } }; //IAnimal {9967F54F-9DE0-43de-ADF9-97ADD45C464C} interface IAnimal : IUnknown { virtual HRESULT __stdcall Eat() = 0; virtual HRESULT __stdcall Speak() = 0; virtual HRESULT __stdcall Play() = 0; }; //***** Class Factory Object ***** class CAnimalFactory : public IClassFactory { public: // *** IUnknown methods *** virtual HRESULT __stdcall QueryInterface(const IID& riid, void** ppvObj); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); // *** IClassFactory methods *** virtual HRESULT __stdcall CreateInstance(IUnknown* pUnkOuter, const IID& riid, void** ppvObject); virtual HRESULT __stdcall LockServer(BOOL bLock); //Constructor CAnimalFactory(); //Destructor ~CAnimalFactory(); private: //Count of reference long m_cRef; }; class CAnimal: public IAnimal { public: // *** IUnknown methods *** virtual HRESULT __stdcall QueryInterface(const IID& riid, void** ppvObj); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); // IAnimal virtual HRESULT __stdcall Eat(); virtual HRESULT __stdcall Speak(); virtual HRESULT __stdcall Play(); //Constructor CAnimal(); //Destructor ~CAnimal(); private: //Reference Count long m_cRef; }; #endif
Dog.cpp
// Dog.cpp #include <objbase.h> #include <iostream.h> #include <assert.h> #include "Dog.h" //global variables static HINSTANCE g_hModule = NULL; // DLL module handler static long g_cComponents = 0; // Count of active components static long g_cServerLocks = 0; // Count of Locks const char g_szFriendlyName[] = "Test Compont, Copyright(C)1999"; const char g_szVerIndProgID[] = "OyamaCom.Dog"; const char g_szProgID[] = "OyamaCom.Dog.1"; //***** Exported Functions ***** BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, void* lpReserved) { if(dwReason == DLL_PROCESS_ATTACH) { g_hModule = hModule; } return TRUE; } STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) { if(clsid != CLSID_DOG) { return CLASS_E_CLASSNOTAVAILABLE; } CDogFactory* pFactory = new CDogFactory; if(pFactory == NULL) { return E_OUTOFMEMORY; } //Get requested interface HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release(); return hr; } STDAPI DllCanUnloadNow() { if((g_cComponents==0) && (g_cServerLocks==0)) { return S_OK; } else { return S_FALSE; } } STDAPI DllRegisterServer() { return RegisterServer(g_hModule, CLSID_DOG, g_szFriendlyName, g_szVerIndProgID, g_szProgID); } STDAPI DllUnregisterServer() { return UnregisterServer(CLSID_DOG, g_szVerIndProgID, g_szProgID); } //***** Class Factory Object ***** // *** IUnknown methods *** HRESULT __stdcall CDogFactory::QueryInterface(const IID& riid, void** ppvObj) { if((riid==IID_IUnknown) || (riid==IID_IClassFactory)) { *ppvObj = static_cast<IClassFactory*>(this); } else { *ppvObj = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppvObj)->AddRef(); return S_OK; } ULONG __stdcall CDogFactory::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG __stdcall CDogFactory::Release() { return InterlockedDecrement(&m_cRef); } // *** IClassFactory methods *** HRESULT __stdcall CDogFactory::CreateInstance(IUnknown* pUnkOuter, const IID& riid, void** ppvObj) { HRESULT hr; //Cannot Aggregate if(pUnkOuter != NULL){ return CLASS_E_NOAGGREGATION; } //Create Component CDog* pDog = new CDog; if(pDog == NULL) { return E_OUTOFMEMORY; } //Initialize an inner class hr = pDog->InitInnerClass(); if(FAILED(hr)) { pDog->Release(); return hr; } //Get the requested interface hr = pDog->QueryInterface(riid, ppvObj); //Release the IUnknown pointer. pDog->Release(); return hr; } HRESULT __stdcall CDogFactory::LockServer(BOOL bLock) { if(bLock) { InterlockedIncrement(&g_cServerLocks); } else { InterlockedDecrement(&g_cServerLocks); } return S_OK; } CDogFactory::CDogFactory() : m_cRef(1) { } CDogFactory::~CDogFactory() { } //***** CDog ***** // *** IUnknown methods *** HRESULT __stdcall CDog::QueryInterface(const IID& riid, void** ppvObj) { if(riid == IID_IUnknown) { *ppvObj = static_cast<IDog*>(this); } else if(riid == IID_IDOG) { *ppvObj = static_cast<IDog*>(this); } else { *ppvObj = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppvObj)->AddRef(); return S_OK; } ULONG __stdcall CDog::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG __stdcall CDog::Release() { return InterlockedDecrement(&m_cRef); } // IAnimal HRESULT __stdcall CDog::Eat() { m_pIAnimal->Eat(); cout << "Excellent!" << endl; return S_OK; } HRESULT __stdcall CDog::Speak() { m_pIAnimal->Speak(); cout << "How are you?" << endl; return S_OK; } HRESULT __stdcall CDog::Play() { m_pIAnimal->Play(); cout << "Play with Dog!" << endl; return S_OK; } //IDog HRESULT __stdcall CDog::Bark() { cout << "Bow Bow Bow" << endl; return S_OK; } //Constructor CDog::CDog() : m_cRef(1), m_pIAnimal(NULL) { InterlockedIncrement(&g_cComponents); } //Destructor CDog::~CDog() { InterlockedDecrement(&g_cComponents); if(m_pIAnimal != NULL){ m_pIAnimal->Release(); } } //Initialize Inner class HRESULT CDog::InitInnerClass() { HRESULT hr = ::CoCreateInstance(CLSID_ANIMAL, NULL, CLSCTX_INPROC_SERVER, IID_IANIMAL, (void**)&m_pIAnimal); if(FAILED(hr)) { return E_FAIL; } else { return S_OK; } }
Dog.h
//Dog.h #include "Animal.h" #if !defined(__DOG__) #define __DOG__ // {48728F16-9BDD-4670-A24A-2A32553D8294} static const GUID CLSID_DOG = { 0x48728f16, 0x9bdd, 0x4670, { 0xa2, 0x4a, 0x2a, 0x32, 0x55, 0x3d, 0x82, 0x94 } }; // {11C37A5F-6C38-46d1-8825-1F1CC3906F5E} static const GUID IID_IDOG = { 0x11c37a5f, 0x6c38, 0x46d1, { 0x88, 0x25, 0x1f, 0x1c, 0xc3, 0x90, 0x6f, 0x5e } }; //IAnimal {9967F54F-9DE0-43de-ADF9-97ADD45C464C} interface IDog : IAnimal { virtual HRESULT __stdcall Bark() = 0; }; //***** Class Factory Object ***** class CDogFactory : public IClassFactory { public: // *** IUnknown methods *** virtual HRESULT __stdcall QueryInterface(const IID& riid, void** ppvObj); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); // *** IClassFactory methods *** virtual HRESULT __stdcall CreateInstance(IUnknown* pUnkOuter, const IID& riid, void** ppvObject); virtual HRESULT __stdcall LockServer(BOOL bLock); //Constructor CDogFactory(); //Destructor ~CDogFactory(); private: //Count of reference long m_cRef; }; class CDog: public IDog { public: // *** IUnknown methods *** virtual HRESULT __stdcall QueryInterface(const IID& riid, void** ppvObj); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); // IAnimal virtual HRESULT __stdcall Eat(); virtual HRESULT __stdcall Speak(); virtual HRESULT __stdcall Play(); // IDog virtual HRESULT __stdcall Bark(); //Constructor CDog(); //Destructor ~CDog(); //Initialize inner class HRESULT __stdcall InitInnerClass(); private: //Pointer to inner component IAnimal* m_pIAnimal; //Reference Count long m_cRef; }; #endifRegisterRelated.h
#if !defined(__REG_RELATED__) #define __REG_RELATED__ //***** Helper Functions ***** HRESULT RegisterServer( HMODULE hModule, // DLL module handle const CLSID& clsid, // Class ID const char* szFriendlyName, // Friendly Name const char* szVerIndProgID, // Programmatic const char* szProgID); // IDs LONG UnregisterServer( const CLSID& clsid, // Class ID const char* szVerIndProgID, // Programmatic const char* szProgID); // IDs // Set the given key and its value. BOOL setKeyAndValue(const char* pszPath, const char* szSubkey, const char* szValue) ; // Convert a CLSID into a char string. void CLSIDtochar(const CLSID& clsid, char* szCLSID, int length) ; // Delete szKeyChild and all of its descendents. LONG recursiveDeleteKey(HKEY hKeyParent, const char* szKeyChild) ; // Size of a CLSID as a string const int CLSID_STRING_SIZE = 39; #endif
参考資料
- 磯田 定宏著『コンピュータ数学シリーズ22 オブジェクト指向モデリング』(コロナ社)
- Martin Fowler, Kendall Scott, 羽生田 栄一訳『UML モデリングのエッセンス 標準オブジェクトモデリング言語の適用』(アジソン・ウェスレイ・パブリッシャーズ・ジャパン株式会社)
- Dale Rogerson 著, バウン グローバル(株)訳『Inside COM』(アスキー出版局)