首页 > 代码库 > 回调方式进行COM组件对外消息传递

回调方式进行COM组件对外消息传递


情景:被调用者--COM组件;调用者---外部程序
作用:COM组件 到 外部程序 的消息传递
方法:
1.外部程序通过接口类对象,访问接口类的方法。COM对象通过连接点方式,进行消息的反向传递。
2.外部程序通过接口类对象,访问接口类的方法。外部程序对接口类设置回调指针,进行消息的回调。

本文讲第二种方法。
直接上代码:

1.添加新的接口类Iww,作为回调函数类。类似连接点对象的作用。

interface Iww : IUnknown{
[helpstring("method Fire_Result")] HRESULT Fire_Result([in] LONG nResult);
};

  

2.原有COM对象接口类,添加一个设置回调函数的方法Advise。

interface Ivv : IUnknown{
[helpstring("method Advise")] HRESULT Advise(Iww* pCallBack, [out] LONG* pdwCookie);
[helpstring("method UnAdvise")] HRESULT UnAdvise(LONG dwCookie);
[helpstring("method Add")] HRESULT Add(LONG n1, LONG n2);
};

  

 

接口类对象:

class ATL_NO_VTABLE Cvv :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<Cvv, &CLSID_vv>,
public Ivv
{
public:
Cvv()
{
for(int i=0; i<10; i++)	// 初始化所有的回调接口为 NULL
{
m_pCallBack[i] = NULL;
}
}

DECLARE_REGISTRY_RESOURCEID(IDR_VV)


BEGIN_COM_MAP(Cvv)
COM_INTERFACE_ENTRY(Ivv)
END_COM_MAP()

 

DECLARE_PROTECT_FINAL_CONSTRUCT()

HRESULT FinalConstruct()
{
return S_OK;
}

void FinalRelease()
{
}

public:

private:
Iww * m_pCallBack[10];
public:
STDMETHOD(Advise)(Iww* pCallBack, LONG* pdwCookie);
STDMETHOD(UnAdvise)(LONG dwCookie);
STDMETHOD(Add)(LONG n1, LONG n2);
};

OBJECT_ENTRY_AUTO(__uuidof(vv), Cvv)

STDMETHODIMP Cvv::Advise(Iww* pCallBack, LONG* pdwCookie)
{
// TODO: Add your implementation code here
if( NULL == pCallBack )
return E_INVALIDARG;

for( int i=0; i<10; i++)	// 寻找一个保存该接口指针的位置
{
if( NULL == m_pCallBack[i] )	// 找到了
{
m_pCallBack[i] = pCallBack;	// 保存到数组中
m_pCallBack[i]->AddRef();	// 指针计数器 +1

*pdwCookie = i + 1;	// cookie 就是数组下标
// +1 的目的是避免使用0,因为0表示无效

return S_OK;
}
}
return E_OUTOFMEMORY;	// 超过10个连接,内存不够用啦
}

STDMETHODIMP Cvv::UnAdvise(LONG dwCookie)
{
// TODO: Add your implementation code here
if( dwCookie<1 || dwCookie>10 )	// 这是谁干的呀?乱给参数
return E_INVALIDARG;

if( NULL == m_pCallBack[ dwCookie - 1 ] )	// 参数错误,或该接口指针已经无效了
return E_INVALIDARG;

m_pCallBack[ dwCookie -1 ]->Release();	// 指针计数器 -1
m_pCallBack[ dwCookie -1 ] = NULL;	// 空出该下标的数组元素

return S_OK;
}

STDMETHODIMP Cvv::Add(LONG n1, LONG n2)
{
// TODO: Add your implementation code here
long nResult = n1 + n2;
for( int i=0; i<10; i++)
{
if( m_pCallBack[i] )	//如果回调接口有效
m_pCallBack[i]->Fire_Result( nResult );	// 则发出事件/通知
}

return S_OK;
}

  

3.外部程序,新建一个类继承Iww,并实现raw_Fire_Result方法。
#import "..\ee.tlb" no_namespace

class CSink :public Iww
{
public:
CSink(void);
~CSink(void);

STDMETHOD(QueryInterface)(const struct _GUID &iid,void ** ppv);
ULONG __stdcall CSink::AddRef(void);
ULONG __stdcall CSink::Release(void);
STDMETHOD(raw_Fire_Result)(long);
};
CSink::CSink(void)
{
}

CSink::~CSink(void)
{
}
// STDMETHODIMP 是宏,等价于 long __stdcall
STDMETHODIMP CSink::QueryInterface(const struct _GUID &iid,void ** ppv)
{
*ppv=this;
return S_OK;
}

ULONG __stdcall CSink::AddRef(void)
{	return 1;	}	// 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

ULONG __stdcall CSink::Release(void)
{	return 0;	}	// 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

STDMETHODIMP CSink::raw_Fire_Result(long nResult)
{	
// 如果完成了连接,当计算有结果后,该函数会被调用。完成组件通知的功能
CString str;
str.Format(_T("%d"),nResult);
MessageBox(str);
return S_OK;
}

  

4.调用步骤

CSink m_sink;
IvvPtr m_spCom;
DWORD m_dwCookie;

HRESULT hr = m_spCom.CreateInstance(__uuidof(vv) );
if( FAILED( hr ) )
{
AfxMessageBox( _T("COM对象初始化失败") );
CDialog::OnCancel();	
}
hr = m_spCom->Advise( &m_sink, (long *)&m_dwCookie );
if( SUCCEEDED( hr ) )
{
AfxMessageBox( _T("Advise调用成功。已经正确连接") );
}
else
{
AfxMessageBox( _T("Advise 调用失败") );
}
m_spCom->Add(1,5);

  

回调方式进行COM组件对外消息传递