COM Client/Interface/Object Structure and Implement

Download Report

Transcript COM Client/Interface/Object Structure and Implement

COM Client/Interface/Object
Architecture and Implement
國立交通大學資訊科學系
分散式系統實驗室
謝欣君
COM Client/Interface/Object



COM Client:直接操作COM物件的程式
COM Object:提供服務供COM Client呼叫
使用之software component,通常又稱為
COM Server
COM Interface:負責溝通COM Client與
COM Object,並且宣告COM Object有那些
服務,使得COM Client知道COM Object
有什麼功能
COM Interface Architecture

在COM中,Interface與Object的對應關係可
以一對多或多對一,亦即m:n的關係
COM Interface1
可以有許多COM Object實作之
COM Interface2
COM Object1
可以實作許多COM Interface
COM Object2
COM Interface/Object 繼承機制



COM Interface採用介面繼承,且只能有
一個father
COM Object採用實作繼承,可以有許多
father(包括Interface、Object)
COM Interface在各物件導向語言中,是
一致的觀念,COM Object則依照程式語
言的特性而有不同的性質
父COM Interface
f1(),f2()
繼承(用IDL的繼承)
子COM Interface
f1(),f2()//繼承來的
f3(),f4()//自己訂的
實作(用C++的繼承)
父COM Interface之
實作:COM Object1
f1(),f2()//必定要實作
實作(用C++的繼承)
也可繼承Object1
的f1(),f2()
子COM Interface之
實作:COM Object2
f1(),f2()//需重新實作
f3(),f4()//必定要實作
IUnknown Interface



提供三個重要的function:QueryInterface()、
AddRef()、Release()
所有的COM Interface都必須繼承此介面
in UNKNOWN.H定義如下:
Interface IUnknown
{
virtual HRESULT __stdcall QueryInterface(const IID& iid,
void** ppv)=0;
virtual ULONG __stdcall AddRef()=0;
virtual ULONG __stdcall Release()=0;
};
QueryInterface的使用
Void foo(Iunknown* pI)
{
IX* pIX=NULL;
//Define a pointer for the interface
//Ask for interface IX
HRESULT hr=pI->QueryInterface(IID_IX,(void**)&pIX);
if (SUCCEEDED(hr))
//Check return value
{
pIX->Fx();
//Use interface
}
}
註:假設pI已知(由COM API或Class Factory在建立物件時得到)
QueryInterface的實作
設條件如下:
interface IX:IUnknown{/*…*/};
interface IY:IUnknown{/*…*/};
class CA:public IX,public IY{/*…*/};
實作如下:
HRESULT __stdcall CA::QueryInterface(const IID& iid,void** ppv)
{
if (iid==IID_IUnknown || iid==IID_IX) { *ppv=(IX*)this; }
else if (iid==IID_IY) { *ppv=(IY*)this; }
else { *ppv=NULL; return E_NOINTERFACE; }
((IUnknown*)*ppv->AddRef();
return S_OK;
}
QueryInterface的運作

實際上QueryInterface是在作Casting的動作
CA::this
IX vtbl pointer
IY
(IX*)CA::this
IY vtbl pointer
(IY*)CA::this
Instance data for CA
Virtual Function Table
QueryInterface
AddRef
Object CA
IX
Release
Fx
CA
QueryInterface
IY* pC=pA;
AddRef
會轉成下列程式碼:
IY* pC=(char*)pA+
Release
IY
Fy
IY
GUID & IID & CLSID

COM Interface的識別元為IID,COM
Object 的識別元為CLSID,兩者皆用GUID
的格式,GUID其實就是OSF DCE的UUID
typedef struct GUID {
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[8];
}GUID;
typedef GUID CLSID;
typedef GUID IID;
註:由上述Microsoft所定義的結構可知共有4+2+2+8=16 bytes=128 bits
Object Life Cycle Management



如果一個function會傳回新的COM
Interface指標,則要呼叫AddRef()
如果copy了一份COM Interface指標,也要
呼叫AddRef(),用完後呼叫Release()
在multi-threaded的環境下,COM Client如
果多一個thread,則必須呼叫AddRef(),結
束時呼叫Release()
Object Life Cycle Management

如果COM Interface指標為全域變數,在欲
使用之Block start處呼叫AddRef(),end處
呼叫Release()
Client 3 COM Client Life Cycle
Client 2
Client 1
A1
A2 R1
A3 R2
COM Object Life Cycle
R3
QueryInterface()特性

假設一個COM Object1實作三個COM
Interface:I1 I2 I3;pI1與pI2與pI3各自為其
指標,則QueryInterface()具下列特性:
– 反射性:pI1->QueryInterface(IID_I1,ppv);必定成立
– 對稱性:pI1->QueryInterface(IID_I2,&pI2);成立後,則
pI2->QueryInterface(IID_I1,&pI1);必定成立
– 遞移性:pI1->QueryInterface(IID_I2,ppv);成立,且
pI2->QueryInterface(IID_I3,ppv);成立,則
pI1->QueryInterface(IID_I3,ppv);必定成立
可扮演的COM角色
COM Client
COM
Interface
COM定
COM定
自定
自定
COM定
自定
COM COM定 自定 COM定 自定COM定自定COM定 自定
Object (1)
(2) (不可能) (4) (5) (6) (不可能) (8)
COM定:COM Defined
自定:Custom Defined
COM Client 程式(操作IMalloc)
class CTest1View : public CScrollView
{
private:
int m_iLines;
int m_iFontHigh;
protected: // create from serialization only
CTest1View();
DECLARE_DYNCREATE(CTest1View)
// Attributes
public:
CTest1Doc* GetDocument();
CPoint m_Lines;
IMalloc *m_pIMalloc;
BOOL m_fAllocations;
// Operations
public:
void
BOOL
BOOL
BOOL
void
………
ShowText(const CString&);
HaveAllocator(void);
DoAllocations(BOOL);
HaveAllocations(void);
FreeAllocations(BOOL);
CTest1View::CTest1View()
{
m_Lines=CPoint(0,0);
m_pIMalloc=NULL;
m_fAllocations=FALSE;
}
CTest1View::~CTest1View()
{
FreeAllocations(TRUE);
CoUninitialize();
}
void CTest1View::OnInitialUpdate()
{
CClientDC dc(this);
TEXTMETRIC m;
dc.GetTextMetrics(&m);
m_iLines=0;
m_iFontHigh=m.tmHeight;
//停止COM服務
SetScrollSizes(MM_TEXT,CSize(1000,2000));
CScrollView::OnInitialUpdate();
CoInitialize(NULL);
//啟動COM服務
}
void CTest1View::OnCogetmalloc()
{
HRESULT hr;
FreeAllocations(TRUE);
hr=CoGetMalloc(1,&m_pIMalloc);//建立Imalloc bject,並取得Interface 指標
if (SUCCEEDED(hr)) ShowText("Allocator OK");
else ShowText("Allocator failed");
}
void CTest1View::OnRelease()
{
if (!HaveAllocator()) {
ShowText("Don't have any allocator");
return;
}
FreeAllocations(TRUE);
ShowText("Release complete");
}
void CTest1View::FreeAllocations(BOOL fRelease)
{
int i;
CTest1Doc* pDoc=GetDocument();
ASSERT_VALID(pDoc);
if (NULL==m_pIMalloc) return;
if (m_fAllocations) {
for (i=0;i<NUM;i++) {
if (NULL!=pDoc->m_ap[i])
m_pIMalloc->Free(pDoc->m_ap[i]);
pDoc->m_av[i]=10;
pDoc->m_ap[i]=NULL;
}
m_fAllocations=FALSE;
}
if (fRelease) {
m_pIMalloc->Release();
m_pIMalloc=NULL;
}
}
執行程式:Malloc.exe
註:欲啟動COM基本服務,要在StdAfx.h中引入ole2.h
自定的COM
Client/Interface/Object 程式
COM Interface:
#ifndef __INTERFACE_H
#define __INTERFACE_H
BOOL CreateObject1(IUnknown **ppUnk);//建立COM Object1並透過ppUnk傳回介面指標
////////////// Common Definition ///////////////
typedef LPVOID* PPVOID;
#define DelleteInterfaceImp(p) {if (NULL!=p) {delete p;p=NULL;}}
#define ReleaseInterface(p) {if (NULL!=p) {p->Release();p=NULL;}}
////////////// Interface Definition /////////////
DECLARE_INTERFACE_(IOne,IUnknown) {
//IUnknown members
STDMETHOD(QueryInterface) (THIS_ REFIID,PPVOID) PURE;
STDMETHOD_(ULONG,AddRef) (THIS) PURE;
STDMETHOD_(ULONG,Release) (THIS) PURE;
//IOne members
STDMETHOD(GetText) (THIS_ LPSTR,int) PURE;
};
typedef IOne* PIone;
#endif
// File:
basetyps.h
#define STDMETHODIMP
HRESULT STDMETHODCALLTYPE
#define STDMETHODIMP_(type)
type STDMETHODCALLTYPE
#define interface struct
#define STDMETHOD(method)
virtual HRESULT STDMETHODCALLTYPE method
#define STDMETHOD_(type,method) virtual type STDMETHODCALLTYPE method
#define PURE
= 0
#define THIS_
#define THIS
void
#define DECLARE_INTERFACE(iface)
interface iface
#define DECLARE_INTERFACE_(iface, baseiface)
interface iface : public
baseiface
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID name \
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
COM Client:
class CTest1View : public CScrollView
{
public:
IUnknown *m_pIUnknown;
………
}
// test1View.cpp : implementation of the CTest1View class
#include <initguid.h>
DEFINE_GUID(IID_IOne,0x12340000,0x43c2,0x11d0,
0xa2,0xa2,0x44,0x45,0x53,0x54,0x0,0x0);
CTest1View::CTest1View()
{
m_pIUnknown=NULL;
}
CTest1View::~CTest1View()
{
ReleaseInterface(m_pIUnknown);
}
void CTest1View::OnObjectcreate()
{
if (NULL!=m_pIUnknown) {
m_pIUnknown->Release();
ShowText("Delete previous interface");
}
if (CreateObject1(&m_pIUnknown))
ShowText("COM Object1 created");
else
ShowText("COM Object1 creation failed");
}
void CTest1View::OnObjectgettext()
{
if (NULL==m_pIUnknown) {
ShowText("There is no object");
return;
}
HRESULT hr;
IOne *pIOne;
const int count=80;
char temp[count];
hr=m_pIUnknown->QueryInterface(IID_IOne,(PPVOID)&pIOne);
if (FAILED(hr)) {
ShowText("Failed to get IOne");
return;
}
hr=pIOne->GetText(temp,count);
if (SUCCEEDED(hr)) ShowText(temp);
else ShowText("IOne::GetText() failed");
pIOne->Release();
}
void CTest1View::OnObjectrelease()
{
if (NULL==m_pIUnknown) {
ShowText("There is no object");
return;
}
if (0==m_pIUnknown->Release()) {
m_pIUnknown=NULL;
ShowText("Object released");
}
}
COM Object:
//file:Object.h
#ifndef __OBJECT1_H
#define __OBJECT1_H
//Actual object using Interface Implementation way
class CObject1:public IOne {
private:
DWORD m_iRef;
public:
CObject1();
~CObject1();
BOOL Init();
//IUnknown members
STDMETHODIMP
QueryInterface(REFIID,PPVOID);
STDMETHODIMP_(DWORD) AddRef();
STDMETHODIMP_(DWORD) Release();
//IOne members
STDMETHODIMP GetText(LPSTR,int);
};
typedef CObject1* PCObject1;
#endif
BOOL CreateObject1(IUnknown **ppUnk) {
HRESULT hr;
CObject1 *pObj;
if (NULL==ppUnk) return FALSE;
pObj=new CObject1;
if (NULL==pObj) return FALSE;
if (!pObj->Init()) return FALSE;
hr=pObj->QueryInterface(IID_IUnknown,(PPVOID)ppUnk);
return SUCCEEDED(hr);
}
STDMETHODIMP CObject1::QueryInterface(REFIID riid,PPVOID ppv)
{
*ppv=NULL;
if (IID_IUnknown==riid || IID_IOne==riid)
*ppv=this;
if (NULL==*ppv) return ResultFromScode(E_NOINTERFACE);
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}
DWORD CObject1::AddRef()
{
return ++m_iRef;
}
DWORD CObject1::Release()
{
if (0!=--m_iRef) return m_iRef;
delete this;
return 0;
}
執行程式: FirstCOM.exe