ActiveX控件的MFC设计之旅

发布时间 : 星期四 文章ActiveX控件的MFC设计之旅更新完毕开始阅读

return lpDispatch->Invoke(dispidMember, riid, lcid, \\

wFlags, pdispparams, pvarResult, \\ pexcepinfo, puArgErr); \\ } \\

/////////////////////////////////////////////////////////////////////

// TRY_DUAL and CATCH_ALL_DUAL are used to provide exception handling // for your dual interface methods. CATCH_ALL_DUAL takes care of // returning the appropriate error code. #define TRY_DUAL(iidSource) \\ HRESULT _hr = S_OK; \\

REFIID _riidSource = iidSource; \\ TRY \\

#define CATCH_ALL_DUAL \\ CATCH(COleException, e) \\ { \\

_hr = e->m_sc; \\ } \\

AND_CATCH_ALL(e) \\ { \\

AFX_MANAGE_STATE(pThis->m_pModuleState); \\ _hr = DualHandleException(_riidSource, e); \\ } \\

END_CATCH_ALL \\ return _hr; \\

///////////////////////////////////////////////////////////////////// // DualHandleException is a helper function used to set the system's // error object, so that container applications that call through // VTBLs can retrieve rich error information

HRESULT DualHandleException(REFIID riidSource, const CException* pAnyException); /////////////////////////////////////////////////////////////////////

// DECLARE_DUAL_ERRORINFO expands to declare the ISupportErrorInfo // support class. It works together with DUAL_ERRORINFO_PART and // IMPLEMENT_DUAL_ERRORINFO defined below. #define DECLARE_DUAL_ERRORINFO() \\

BEGIN_INTERFACE_PART(SupportErrorInfo, ISupportErrorInfo) \\ STDMETHOD(InterfaceSupportsErrorInfo)(THIS_ REFIID riid); \\ END_INTERFACE_PART(SupportErrorInfo) \\

/////////////////////////////////////////////////////////////////////

// DUAL_ERRORINFO_PART adds the appropriate entry to the interface map // for ISupportErrorInfo, if you used DECLARE_DUAL_ERRORINFO. #define DUAL_ERRORINFO_PART(objectClass) \\

INTERFACE_PART(objectClass, IID_ISupportErrorInfo, SupportErrorInfo) \\ /////////////////////////////////////////////////////////////////////

// IMPLEMENT_DUAL_ERRORINFO expands to an implementation of // ISupportErrorInfo which matches the declaration in // DECLARE_DUAL_ERRORINFO.

#define IMPLEMENT_DUAL_ERRORINFO(objectClass, riidSource) \\

STDMETHODIMP_(ULONG) objectClass::XSupportErrorInfo::AddRef() \\ { \\

METHOD_PROLOGUE(objectClass, SupportErrorInfo) \\ return pThis->ExternalAddRef(); \\ } \\

STDMETHODIMP_(ULONG) objectClass::XSupportErrorInfo::Release() \\ { \\

METHOD_PROLOGUE(objectClass, SupportErrorInfo) \\ return pThis->ExternalRelease(); \\ } \\

STDMETHODIMP objectClass::XSupportErrorInfo::QueryInterface( \\ REFIID iid, LPVOID* ppvObj) \\ { \\

METHOD_PROLOGUE(objectClass, SupportErrorInfo) \\ return pThis->ExternalQueryInterface(&iid, ppvObj); \\ } \\

STDMETHODIMP objectClass::XSupportErrorInfo::InterfaceSupportsErrorInfo( \\ REFIID iid) \\ { \\

METHOD_PROLOGUE(objectClass, SupportErrorInfo) \\ return (iid == riidSource) ? S_OK : S_FALSE; \\ }

下面是jiangsheng(蒋晟.MSMVP2004Jan)老大的精彩回贴:

Knowledge Base Article Q157437: \ Fires Events from a Second Thread\

http://support.microsoft.com/support/kb/articles/q157/4/37.asp

Knowledge Base Article Q196026: \ Firing Event in Second Thread Causes IPF or GPF\

http://support.microsoft.com/support/kb/articles/q196/0/26.asp

Michael Lindig's ATL: Firing events from worker threads article on CodeGuru.com

http://www.codeguru.com/atl/ThreadEvents.shtml

Knowledge Base Article Q280512: \ ATLCPImplMT Encapsulates ATL Event Firing Across COM Apartments\

http://support.microsoft.com/support/kb/articles/q280/5/12.asp

COM FAQ 11: HOWTO: Post messages to a hidden window for raising events from an Apartment-threaded object employing worker threads http://www.mvps.org/vcfaq/com/11.htm

这是从csdn上摘来的一段(忘了原贴位置了,很是抱歉),主要是解决ActiveX控件如何从工作线程中触发事件的,因为自己以前也遇到过这个问题,所以今天又给翻来出来。

上面提到的解决方法有许多都是针对ATL控件的,虽然MFC下也应该可以用,不过似乎太过劳师动众了,所以这里选择了microsoft的Fireev.exe例程中的方法来重历一回使用隐藏窗口来触发事件的方法,可以参考Fireev.exe例程

1.新建一无窗口(windowless activation)控件TFire,添加三方法void Start(),void End()和void Trigger(LPCTSTR strParam)和一事件void FireThreadEvent(LPCTSTR strEvent)。这里Start用来开始工作线程,End用来结束工作线程,Trigger用来使工作线程产生事件,ThreadEvent就是所产生的事件名。 整个思路如下:

在CTFireCtrl的Start方法中,创建线程MyThread,创建隐藏窗口CMyWindow,在 Trigger方法中,通过SetEvent 通知线程PostMessage给隐藏窗口,再由CMyWindow调用CTFireCtrl的FireTheadEvent触发事件,当调用 CTFireCtr的End方法时,就结束MyThead线程,销毁隐藏窗口

2.先从CWnd派生一CMyWindow类,用作隐藏窗口,

a.添加一成员变量CTFireCtrl* m_pCtrl,用来实际调用CTFireCtrl的FireThreadEvent来产生事件。

b.定义自定义消息define WM_THREADEVENT WM_USER+101。

c.添加自定义消息处理函数 LRESULT OnFireThreadEvent(WPARAM wParam, LPARAM lParam);并加入到消息映射宏中,如下:

BEGIN_MESSAGE_MAP(CMyWindow, CWnd) //{{AFX_MSG_MAP(CMyWindow)

// NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP

ON_MESSAGE(WM_THREADEVENT, OnFireThreadEvent) END_MESSAGE_MAP()

OnFireThreadEvent函数定义如下,这里得首先将CTFireCtrl的FireThreadEvent从protected改为public,当然也可以在CTFireCtrl中新建一函数,在该函数中Fire,不过多折腾了一番,这里用和Fireev例程中相同的方法:

LRESULT CMyWindow::OnFireThreadEvent(WPARAM wParam, LPARAM lParam) {

m_pCtrl->FireThreadEvent((LPCTSTR)lParam); return 0; }

d.添加HWND Create()成员函数,以创建窗口,并返回窗口句柄供线程PostMessage使用。函数定义如下:

HWND CMyWindow::Create() {

//Register a window class LPCTSTR classname = 0;

classname = AfxRegisterWndClass(0);

//Create the window and return it's handle

CWnd::CreateEx(NULL,classname,NULL,NULL,1,1,1,1,NULL,NULL); ASSERT(m_hWnd!=NULL); return m_hWnd; }

e.重写PostNcDestroy虚拟函数,以在窗口销毁时删除CMyWindow类对象 void CMyWindow::PostNcDestroy() {

// TODO: Add your specialized code here and/or call the base class CWnd::PostNcDestroy(); delete this; }

3.定义线程函数

DWORD MyThread(LPVOID pParam) {

CTFireCtrl* pctrl = (CTFireCtrl*)pParam; while(!pctrl->m_bEnd){

DWORD dwRes = WaitForSingleObject(pctrl->m_hEvent, 100); if(dwRes == WAIT_OBJECT_0){

PostMessage(pctrl->m_hMyWnd, WM_THREADEVENT, 0, (LPARAM)LPCTSTR(pctrl->m_strParam)); } }

return 0; }

4.可以看到线程函数中需要用到很多变量,因此在CTFireCtrl中定义成员变量如下: BOOL m_bEnd;//控制线程是否结束

HANDLE m_hEvent;//事件句柄,用途就不多说了

HWND m_hMyWnd;//CMyWindow的窗口句柄,由它的Create函数返回获得。

CString m_strParam;//用来传递给事件参数的字符串,仅作参考用,由Trigger方法赋值获得

5.定义Start函数 void CTFireCtrl::Start() {

// TODO: Add your dispatch handler code here //初始化传递给线程的各个变量 m_bEnd = FALSE;

m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //建立传递事件用的隐藏窗口

CMyWindow* pwnd = new CMyWindow; pwnd->m_pCtrl = this;

联系合同范文客服:xxxxx#qq.com(#替换为@)