IDL中提供的[source]属性,是为了让实现类宣扬它支持哪几个对外接口注册。
主要表达的意思的是--组件内部在某种条件下将调用该接口,向外界传递信息。如果你对此信息感兴趣,就要自己实现这个接口,并向该组件注册并连接。这样当组件调用该接口时,会向你及时传递你感兴趣的信息。
例如:
coclass Math
{
[default] interface IMath;
[source, default] dispinterface _IMathEvents; <==== 对外宣扬 Math组件支持_IMathEvents对外接口的注册。
};
com包括引入接口和引出接口,来描述组件可以支持的两种不同类型的接口。
. 一个引入接口是指由组件实现的接口,这里的IMath接口就是一个引入接口。因为它是有组件来实现的。
.一个引出接口是指在组件的类型库中描述的接口,这里的_IMathEvents接口就是一个引入接口。它实际上是由Math组件的客户机来实现的。
下面展示_IMathEvents接口是如何实现,并与组件Math组件注册并链接的。
public CComObjectRoot,
public _IMathEvents
{
public:
CMathEvents()
{
}
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(_IMathEvents)
END_COM_MAP()
public:
STDMETHODIMP GetTypeInfoCount(UINT*)
{
return E_NOTIMPL;
}
LCID lcid,
ITypeInfo **ppTInfo)
{
return E_NOTIMPL;
}
LPOLESTR *rgszNames,
UINT cNames,
LCID lcid,
DISPID *rgDispId)
{
return E_NOTIMPL;
}
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pDispParams,
VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,
UINT *puArgErr)
{
switch( dispIdMember )
{
case 0x1:
// Make sure the is just one argument
if ( pDispParams->cArgs != 1 )
return DISP_E_BADPARAMCOUNT;
if ( pDispParams->cNamedArgs )
return DISP_E_NONAMEDARGS;
HRESULT hr;
VARIANTARG var;
VariantInit( &var );
hr = VariantChangeTypeEx( &var,
&(pDispParams->rgvarg[0]),
lcid, 0, VT_I4 );
if FAILED( hr )
return DISP_E_BADVARTYPE;
break;
default:
DisplayMessage( "Error" );
break;
}
return S_OK;
}
STDMETHODIMP ComputationComplete(long lResult)
{
char szMsg[128];
sprintf( szMsg, "The result is %d", lResult );
DisplayMessage( szMsg );
return S_OK;
}
};
HRESULT hr;
hr = CoCreateInstance( CLSID_Math, <==== 创建宣扬支持_IMathEvents对外接口的组件
NULL,
CLSCTX_SERVER,
IID_IMath,
( void ** ) & ptrMath );
if ( FAILED( hr ))
{
HandleError( " Failed to create server instance " , hr );
return - 1 ;
}
CComObject < CMathEvents >* ptrMathEvents;
CComPtr < IUnknown > ptrEventsUnk = ptrMathEvents;
IMathEvents *pMathEvents;
hr = ptrMathEvents->QueryInterface(IID_IMathEvents, (void **)&pMathEvents); <=======获得_IMathEvents接口指针
// Set up the connection
DWORD dwCookie;
hr = AtlAdvise( ptrMath,
pMathEvents,
DIID__IMathEvents,
& dwCookie ); <======将_IMathEvents接口指针pMathEvents在ptrMath组件实例中注册并链接
if (FAILED( hr ))
{
HandleError( " Unable to setup the connection for IMathEvents " , hr );
return - 1 ;
}
// Access the IMath interface
long lResult;
ptrMath -> Add( 300 , 10 , & lResult ); <========调用ptrMath组件方法。组件方法执行过程中会适时通过传入的_IMathEvents接口指针pMathEvents向调用者返回信息--回调。
ptrMath -> Subtract( 300 , 10 , & lResult );
ptrMath -> Multiply( 300 , 10 , & lResult );
ptrMath -> Divide( 300 , 10 , & lResult );
// Shut down the event connection
AtlUnadvise( ptrMath,
DIID__IMathEvents,
dwCookie ); <======中断_IMathEvents接口指针与ptrMath组件实例的链接
.....
其中AtlAdvise和AtlUnadvise的实现过程是这样的:
spSource = /* Set to the source of the events */ ;
CComPtr < _ISpeakerEvents >
spSink = /* Sink to receive the events */ ;
DWORD dwCookie ;
CComPtr < IConnectionPointContainer > spcpc;
HRESULT hr = spSource.QueryInterface ( & spcpc);
CComPtr < IConnectionPoint > spcp ;
hr = spcpc -> FindConnectionPoint(__uuidof(_ISpeakerEvents),
& spcp); <======根据_ISpeakerEvents来查找ConnectionPoint。只有组件宣扬的对外接口,才可以查找成功。
hr = spcp -> Advise (spSink, & dwCookie) ; // Establish connection
// Time goes by, callbacks occur...
hr = spcp -> Unadvise (dwCookie) ; // Break connection
hr = AtlAdvise( ptrMath, // 对外宣扬 支持_IMathEvents接口
pMathEvents, // _IMathEvents类型接口指针
DIID__IMathEvents,
&dwCookie );
结论:对于AtlAdvise,只有组件宣扬的对外接口,才可以注册成功。