最近看公司的一些新產品和框架 , 發現清一色的「COM思想架構 」, 這裏說的「COM思想架構」是指不徹底是標準COM組件的方式,而是指在設計上用到了COM思想。
DllGetClassObject返回IClassFactory指針,而後咱們就能夠經過該接口的CreateInstance方法建立對象並取得所需的接口。
咱們能夠看到標準COM組件很是強大, 可是不少時候咱們並不須要標準COM組件的全部特性,好比咱們不但願引入註冊表, 也不但願引入COM運行庫,咱們但願咱們的程序是徹底「綠色」的。這時咱們就會採用「COM思想架構「開發非標準的COM組件。
調用這些導出的CreateXXX函數返回返回一個繼承於IUnknown的接口, 而後咱們就能夠調用接口提供的方法了, 能夠看下IXmlReader的方法:
IXmlReader :
public
IUnknown
{
public
:
virtual
HRESULT STDMETHODCALLTYPE SetInput(
/*
[annotation]
*/
__in_opt IUnknown
*
pInput)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE GetProperty(
/*
[annotation]
*/
__in UINT nProperty,
/*
[annotation]
*/
__out LONG_PTR
*
ppValue)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE SetProperty(
/*
[annotation]
*/
__in UINT nProperty,
/*
[annotation]
*/
__in_opt LONG_PTR pValue)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE Read(
/*
[annotation]
*/
__out_opt XmlNodeType
*
pNodeType)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE GetNodeType(
/*
[annotation]
*/
__out XmlNodeType
*
pNodeType)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE MoveToFirstAttribute(
void
)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE MoveToNextAttribute(
void
)
=
0
;
.......
};
關因而微軟下一代2D渲染接口, 關於它的詳情參考
Direct2D, 咱們一樣分析一下它的導出函數:
實際看到這裏也用了COM思想的方法,咱們能夠看看D2D1CreateFactory返回的ID2D1Factory的接口:
interface
DX_DECLARE_INTERFACE(
"
06152247-6f50-465a-9245-118bfd3b6007
"
) ID2D1Factory :
public
IUnknown
{
//
//
Cause the factory to refresh any system metrics that it might have been snapped
//
on factory creation.
//
STDMETHOD(ReloadSystemMetrics)(
) PURE;
//
//
Retrieves the current desktop DPI. To refresh this, call ReloadSystemMetrics.
//
STDMETHOD_(
void
, GetDesktopDpi)(
_Out_ FLOAT
*
dpiX,
_Out_ FLOAT
*
dpiY
) PURE;
STDMETHOD(CreateRectangleGeometry)(
_In_ CONST D2D1_RECT_F
*
rectangle,
_Outptr_ ID2D1RectangleGeometry
**
rectangleGeometry
) PURE;
STDMETHOD(CreateRoundedRectangleGeometry)(
_In_ CONST D2D1_ROUNDED_RECT
*
roundedRectangle,
_Outptr_ ID2D1RoundedRectangleGeometry
**
roundedRectangleGeometry
) PURE;
STDMETHOD(CreateEllipseGeometry)(
_In_ CONST D2D1_ELLIPSE
*
ellipse,
_Outptr_ ID2D1EllipseGeometry
**
ellipseGeometry
) PURE;
......
};
//
interface ID2D1Factory
思考爲何會有愈來愈多的新程序採用這種」COM思想架構「, 這個要回到COM的根 ---- IUnknown接口:
IUnknown
{
public
:
BEGIN_INTERFACE
virtual
HRESULT STDMETHODCALLTYPE QueryInterface(
/*
[in]
*/
REFIID riid,
/*
[annotation][iid_is][out]
*/
__RPC__deref_out
void
**
ppvObject)
=
0
;
virtual
ULONG STDMETHODCALLTYPE AddRef(
void
)
=
0
;
virtual
ULONG STDMETHODCALLTYPE Release(
void
)
=
0
;
END_INTERFACE
};
IUnknow接口是個偉大的創造!
IUnknow的AddRef和Release實現對象的引用計數管理, 引用計數用來管理對象的生存週期。
經過引用計數一來能夠很方便的共享對象, 另外也能確保對象被正確釋放(確保對象的new和delete在同一模塊中)。
QueryInterface實現接口查詢, 經過這種方式能夠很方便的對現有組件進行升級, 只要接口不改 ,能夠隨意修改內部實現而不用客戶程序從新編譯。
另外也能夠直接增長新接口, 只要在QueryInterface內增長並能夠查詢到該新接口, 咱們就能夠調用該新接口。
咱們能夠看到QueryInterface讓C++這種靜態語言有了某些動態語言的特性, 在C# 中咱們能夠經過反射查詢到某個類的成員函數和成員變量, Objective-C中咱們也能夠根據函數名動態調用某個函數, 在腳本語言中,咱們能夠在運行時動態查詢和修改某個類的信息。經過COM的QueryInterface, 咱們能夠動態查詢某個組件類實現哪些接口(函數)。固然他們之間有本質的區別, 動態語言運行時內存中保存有類信息, 而C++的QueryInterface經過switch case, 返回的是存有虛表指針的對象指針。
最後再簡單談下IUnknown的升級版IDispatch和IInspectable。
先看IDispatch:
IDispatch :
public
IUnknown
{
public
:
virtual
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/*
[out]
*/
__RPC__out UINT
*
pctinfo)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE GetTypeInfo(
/*
[in]
*/
UINT iTInfo,
/*
[in]
*/
LCID lcid,
/*
[out]
*/
__RPC__deref_out_opt ITypeInfo
**
ppTInfo)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/*
[in]
*/
__RPC__in REFIID riid,
/*
[size_is][in]
*/
__RPC__in_ecount_full(cNames) LPOLESTR
*
rgszNames,
/*
[range][in]
*/
UINT cNames,
/*
[in]
*/
LCID lcid,
/*
[size_is][out]
*/
__RPC__out_ecount_full(cNames) DISPID
*
rgDispId)
=
0
;
virtual
/*
[local]
*/
HRESULT STDMETHODCALLTYPE Invoke(
/*
[in]
*/
DISPID dispIdMember,
/*
[in]
*/
REFIID riid,
/*
[in]
*/
LCID lcid,
/*
[in]
*/
WORD wFlags,
/*
[out][in]
*/
DISPPARAMS
*
pDispParams,
/*
[out]
*/
VARIANT
*
pVarResult,
/*
[out]
*/
EXCEPINFO
*
pExcepInfo,
/*
[out]
*/
UINT
*
puArgErr)
=
0
;
};
IDispatch繼承於IUnknown, 經過IDispatch, 咱們能夠實現腳本語言對COM組件的調用,咱們能夠經過GetTypeInfo獲取對象的類型信息, 經過GetIDsOfNames函數以字符串的方式獲取函數的DISPID, 經過Invoke動態調用某個函數。IE的DOM對象與JS的交互所有是經過IDispatch(Ex)接口實現的。固然,除非你的組件要與腳本語言交互, 否者通常不用實現該接口。
再看IInspectable:
IInspectable :
public
IUnknown
{
public
:
virtual
HRESULT STDMETHODCALLTYPE GetIids(
/*
[out]
*/
__RPC__out ULONG
*
iidCount,
/*
[size_is][size_is][out]
*/
__RPC__deref_out_ecount_full_opt(
*
iidCount) IID
**
iids)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE GetRuntimeClassName(
/*
[out]
*/
__RPC__deref_out_opt HSTRING
*
className)
=
0
;
virtual
HRESULT STDMETHODCALLTYPE GetTrustLevel(
/*
[out]
*/
__RPC__out TrustLevel
*
trustLevel)
=
0
;
};
IInspectable也繼承於IUnknown, 它是WinRT全部對象的基接口, 因此WinRT仍是基於COM技術。
其中GetTrustLevel返回信任等級, GetRuntimeClassName返回類名, 而GetIids返回當前類對象實現了哪些接口(全部接口的iid), 獲得接口的iid後, 咱們就能夠經過QueryInterface查詢咱們須要的接口了, 獲得接口指針就能夠調用內部函數了。
最後總結下,回答下文章開頭的問題, 不少人說COM過期了, 也許」純正的標準COM「確實是使用的人愈來愈少了, 可是COM的思想卻一直在後續的軟件開發中被使用和發揚, 能夠說COM技術是微軟技術框架的「根」(之一)。