継承の方法

このページはかなり昔に書いた資料です。今 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;
};

#endif
RegisterRelated.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』(アスキー出版局)

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説