基本構成
プロセスの壁は跨がず、DLLをregsvr32コマンドでレジストリに登録しておき、それをCoCreateInstance
する。まあつまりインプロセスサーバー。
まずはATLを使わない例
のコードを動かして遊んでみる。
// ヘッダファイルのインクルード
#include <windows.h> // 標準WindowsAPI
#include <cstdio> // 標準入出力
#include <tchar.h> // TCHAR対応
#include <iostream>
#include "IUnknown_.h" // MIDL生成
#include "IUnknown__i.c" // GUID
// _tmain関数の定義
int _tmain(int argc, TCHAR *argv[]){ // main関数のTCHAR版.
IUnknown_* pUnknown = NULL;
CoInitialize(NULL);
std::cout << "call CoCreateInstance" << std::endl;
HRESULT hr = CoCreateInstance(CLSID_CUnknown, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown_, (void **)&pUnknown);
if (FAILED(hr)){
_tprintf(_T("CoCreateInstance failed!\n"));
CoUninitialize();
return 0;
}
std::cout << "call pUnknown->Method" << std::endl;
pUnknown->Method();
std::cout << "call pUnknown->Release" << std::endl;
pUnknown->Release();
CoUninitialize();
// プログラムの終了
return 0;
}
call CoGetClassObject
CClassFactory::CClassFactory, m_lRef=1
CClassFactory::QueryInterface!
CClassFactory::AddRef, m_lRef=2
CClassFactory::AddRef, m_lRef=3
CClassFactory::Release, m_lRef=2
CClassFactory::QueryInterface!
CClassFactory::AddRef, m_lRef=3
CClassFactory::Release, m_lRef=2
call pcf->CreateInstance
CClassFactory::CreateInstance
CUnknown::QueryInterface!
CUnknown::AddRef, m_lRef=2
CUnknown::Release, m_lRef=1
call pcf->Release
CClassFactory::Release, m_lRef=1
call pUnknown->Method
CUnknown::Method!
call pUnknown->Release
CUnknown::Release, m_lRef=0
CClassFactory::~CClassFactory, m_lRef=1
さて、ここで、
$$ CoCreateInstance = CoGetClassObject + CreateInstance + Release $$
となるはずなので試してみる。
// ヘッダファイルのインクルード
#include <windows.h> // 標準WindowsAPI
#include <cstdio> // 標準入出力
#include <tchar.h> // TCHAR対応
#include <iostream>
#include "IUnknown_.h" // MIDL生成
#include "IUnknown__i.c" // GUID
// _tmain関数の定義
int _tmain(int argc, TCHAR *argv[]){ // main関数のTCHAR版.
IUnknown_* pUnknown = NULL;
CoInitialize(NULL);
std::cout << "call CoCreateInstance" << std::endl;
HRESULT hr = CoCreateInstance(CLSID_CUnknown, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown_, (void **)&pUnknown);
if (FAILED(hr)){
_tprintf(_T("CoCreateInstance failed!\n"));
CoUninitialize();
return 0;
}
std::cout << "call pUnknown->Method" << std::endl;
pUnknown->Method();
std::cout << "call pUnknown->Release" << std::endl;
pUnknown->Release();
CoUninitialize();
// プログラムの終了
return 0;
}
call CoCreateInstance
CClassFactory::CClassFactory, m_lRef=1
CClassFactory::QueryInterface!
CClassFactory::AddRef, m_lRef=2
CClassFactory::CreateInstance
CUnknown::QueryInterface!
CUnknown::AddRef, m_lRef=2
CUnknown::Release, m_lRef=1
CUnknown::AddRef, m_lRef=2
CUnknown::Release, m_lRef=1
CClassFactory::Release, m_lRef=1
CUnknown::QueryInterface!
CUnknown::AddRef, m_lRef=2
CUnknown::Release, m_lRef=1
call pUnknown->Method
CUnknown::Method!
call pUnknown->Release
CUnknown::Release, m_lRef=0
CClassFactory::~CClassFactory, m_lRef=1
difffで比較するとこんな感じ。
CoCreateInstanceした場合は、CClassFactoryの参照カウント操作がサーバー側で行われるため、CoGetClassObject + CreateInstance + Releaseで2通信するのに比べ、1通信で済んでいることがわかる。
ATLで書いてみる
ATL初心者なのでとりあえず手元に転がってたVisualStudio2015でポチポチとATLのプロジェクトを作った。
// IClassFactory2.idl : IClassFactory2 の IDL ソース
//
// このファイルは、タイプ ライブラリ (IClassFactory2.tlb) およびマーシャリング コードを
// 作成するために MIDL ツールによって処理されます。
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(4670985E-D857-46F5-9FFB-83B5032852DA),
dual,
nonextensible,
pointer_default(unique)
]
interface ICUnknown : IDispatch{
HRESULT Method(void);
};
[
uuid(ACCD544C-42AC-4A37-9300-DEBE5626ADE7),
version(1.0),
]
library IClassFactory2Lib
{
importlib("stdole2.tlb");
[
uuid(4F1586EA-BF58-423D-81D9-19DD6248DFE4)
]
coclass CUnknown
{
[default] interface ICUnknown;
};
};
// CCUnknown
class ATL_NO_VTABLE CCUnknown :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CCUnknown, &CLSID_CUnknown>,
public IDispatchImpl<ICUnknown, &IID_ICUnknown, &LIBID_IClassFactory2Lib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
CCUnknown();
DECLARE_REGISTRY_RESOURCEID(IDR_CUNKNOWN)
DECLARE_NOT_AGGREGATABLE(CCUnknown)
BEGIN_COM_MAP(CCUnknown)
COM_INTERFACE_ENTRY(ICUnknown)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
//DECLARE_PROTECT_FINAL_CONSTRUCT()
void InternalFinalConstructAddRef();
void InternalFinalConstructRelease();
HRESULT FinalConstruct();
void FinalRelease();
public:
STDMETHODIMP Method() override;
};
OBJECT_ENTRY_AUTO(__uuidof(CUnknown), CCUnknown)
// CUnknown.cpp : CCUnknown の実装
#include "stdafx.h"
#include "CUnknown.h"
#include <iostream>
// CCUnknown
CCUnknown::CCUnknown()
{
std::cout << "CCUnknown::CCUnknown" << std::endl;
}
//DECLARE_PROTECT_FINAL_CONSTRUCT()
void CCUnknown::InternalFinalConstructAddRef()
{
std::cout << "CCUnknown::InternalFinalConstructAddRef" << std::endl;
InternalAddRef();
}
void CCUnknown::InternalFinalConstructRelease()
{
std::cout << "CCUnknown::InternalFinalConstructRelease" << std::endl;
InternalRelease();
}
HRESULT CCUnknown::FinalConstruct()
{
std::cout << "CCUnknown::FinalConstruct" << std::endl;
return S_OK;
}
void CCUnknown::FinalRelease()
{
std::cout << "CCUnknown::FinalRelease" << std::endl;
}
STDMETHODIMP CCUnknown::Method()
{
std::cout << "CCUnknown::Method" << std::endl;
return S_OK;
}
これをATLを使わないプロジェクトから先程と同様に呼び出してみる。めんどくさいのでCoGetClassObject+CreateInstance+Relase版とCoCreateInstance版は#if
で切り替えることにした。
// Main.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//
#include "stdafx.h"
#include <IClassFactory2_i.h>
#include <IClassFactory2_i.c>
#include <iostream>
int main()
{
ICUnknown* pUnknown = NULL;
CoInitialize(NULL);
#if 1
IClassFactory* pcf = NULL;
std::cout << "call CoGetClassObject" << std::endl;
HRESULT hr1 = CoGetClassObject(CLSID_CUnknown, CLSCTX_INPROC_SERVER, 0, IID_IClassFactory, reinterpret_cast<void**>(&pcf));
if (FAILED(hr1)) {
_tprintf(_T("CoGetClassObject failed!\n"));
CoUninitialize();
return 0;
}
std::cout << "call pcf->CreateInstance" << std::endl;
HRESULT hr2 = pcf->CreateInstance(NULL, IID_ICUnknown, reinterpret_cast<void**>(&pUnknown));
std::cout << "call pcf->Release" << std::endl;
pcf->Release();
if (FAILED(hr2)) {
_tprintf(_T("CreateInstance failed!\n"));
CoUninitialize();
return 0;
}
#else
std::cout << "call CoCreateInstance" << std::endl;
HRESULT hr = CoCreateInstance(CLSID_CUnknown, NULL, CLSCTX_INPROC_SERVER, IID_ICUnknown, (void **)&pUnknown);
if (FAILED(hr)) {
_tprintf(_T("CoCreateInstance failed!\n"));
CoUninitialize();
return 0;
}
#endif
std::cout << "call pUnknown->Method" << std::endl;
pUnknown->Method();
std::cout << "call pUnknown->Release" << std::endl;
pUnknown->Release();
CoUninitialize();
// プログラムの終了
return 0;
}
call CoGetClassObject
DllGetClassObject
call pcf->CreateInstance
CCUnknown::CCUnknown
CCUnknown::InternalFinalConstructAddRef, m_dwRef=0
CCUnknown::FinalConstruct
CCUnknown::InternalFinalConstructRelease, m_dwRef=1
call pcf->Release
call pUnknown->Method
CCUnknown::Method
call pUnknown->Release
CCUnknown::FinalRelease
ちゃんと呼び出せている。
DECLARE_PROTECT_FINAL_CONSTRUCT
このマクロは次のように定義されている。
#define DECLARE_PROTECT_FINAL_CONSTRUCT()\
void InternalFinalConstructAddRef() {InternalAddRef();}\
void InternalFinalConstructRelease() {InternalRelease();}
今回はログを埋め込みたかったので同等の内容を手動で定義している。
ではこれを定義しない場合何が起こるのだろうか。
今回作成したCCUnknown
クラスの継承関係は次のようになっている。
つまりCComObjectRootBase
で定義されている次の定義が用いられるはずだ。
void InternalFinalConstructAddRef()
{
}
void InternalFinalConstructRelease()
{
ATLASSUME(m_dwRef == 0);
}
実験しよう。ログを埋め込みたいので明示的に基底クラスの関数を呼び出すようにしてみる
void CCUnknown::InternalFinalConstructAddRef()
{
std::cout << "CCUnknown::InternalFinalConstructAddRef, m_dwRef=" << m_dwRef << std::endl;
base::InternalFinalConstructAddRef();
}
void CCUnknown::InternalFinalConstructRelease()
{
std::cout << "CCUnknown::InternalFinalConstructRelease, m_dwRef=" << m_dwRef << std::endl;
base::InternalFinalConstructRelease();
}
結果は次の通り。
call CoGetClassObject
DllGetClassObject
call pcf->CreateInstance
CCUnknown::CCUnknown
CCUnknown::InternalFinalConstructAddRef, m_dwRef=0
CCUnknown::FinalConstruct
CCUnknown::InternalFinalConstructRelease, m_dwRef=0
call pcf->Release
call pUnknown->Method
CCUnknown::Method
call pUnknown->Release
CCUnknown::FinalRelease
うーん?
DECLARE_NOT_AGGREGATABLE
vs DECLARE_POLY_AGGREGATABLE
試しにDECLARE_POLY_AGGREGATABLE
に切り替えてみる
call CoGetClassObject
DllGetClassObject
call pcf->CreateInstance
CCUnknown::CCUnknown
CCUnknown::FinalConstruct
call pcf->Release
call pUnknown->Method
CCUnknown::Method
call pUnknown->Release
CCUnknown::FinalRelease
あれ、InternalFinalConstructAddRef
/InternalFinalConstructRelease
はいずこに・・・?
どうもCCUnknown
のInternalFinalConstructAddRef
/InternalFinalConstructRelease
はすっ飛ばされている感じがある。
#define DECLARE_POLY_AGGREGATABLE(x) public:\
typedef ATL::CComCreator< ATL::CComPolyObject< x > > _CreatorClass;
より、CComCreator
のCreateInstanceが呼ばれ、
template <class T1>
class CComCreator
{
public:
static HRESULT WINAPI CreateInstance(
_In_opt_ void* pv,
_In_ REFIID riid,
_COM_Outptr_ LPVOID* ppv)
{
ATLASSERT(ppv != NULL);
if (ppv == NULL)
return E_POINTER;
*ppv = NULL;
HRESULT hRes = E_OUTOFMEMORY;
T1* p = NULL;
ATLPREFAST_SUPPRESS(6014 28197)
/* prefast noise VSW 489981 */
ATLTRY(p = _ATL_NEW T1(pv))
ATLPREFAST_UNSUPPRESS()
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->_AtlInitialConstruct();
if (SUCCEEDED(hRes))
hRes = p->FinalConstruct();
if (SUCCEEDED(hRes))
hRes = p->_AtlFinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK)
{
hRes = p->QueryInterface(riid, ppv);
_Analysis_assume_(hRes == S_OK || FAILED(hRes));
}
if (hRes != S_OK)
delete p;
}
return hRes;
}
};
T1
はCComPolyObject<CCUnknown>
であるから
CCUnknown
ではなく、継承関係をたどったCComObjectRootBase
のそれがよびだされるのであろう。
整理すると、DECLARE_POLY_AGGREGATABLE
を指定した場合、対象クラスにDECLARE_PROTECT_FINAL_CONSTRUCT
を指定しても効果は発揮されないということが言える。
続編