ActiveX控件的MFC设计之旅-第5步 .

 下面是jiangsheng(蒋晟.MSMVP2004Jan)老大的精彩回贴:  
 
Knowledge  Base  Article  Q157437:  "Fireev.exe  Fires  Events  from  a  Second  Thread"    
http://support.microsoft.com/support/kb/articles/q157/4/37.asp  
Knowledge  Base  Article  Q196026:  "PRB:  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:  "SAMPLE:  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;
    m_hMyWnd = pwnd->Create();

    //建立工作线程
    DWORD dwID;
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)MyThread, this, NULL, &dwID);

}

6.定义Trigger方法

void CTFireCtrl::Trigger(LPCTSTR strParam)
{
    // TODO: Add your dispatch handler code here
    m_strParam = strParam;
    if(m_hEvent){
        SetEvent(m_hEvent);
    }
}

7.定义End方法

void CTFireCtrl::End()
{
    // TODO: Add your dispatch handler code here
    m_bEnd = TRUE;
    Sleep(100);
    CloseHandle(m_hEvent);
    m_hEvent = NULL;
    ::DestroyWindow(m_hMyWnd);
}

8.应该是可以了,编译一下,开始VB工程
在VB的Form1中加一控件TFire1,一按钮Command1
代码如下:
Private Sub Command1_Click()
TFire1.Trigger "Hello"
End Sub

Private Sub Form_Load()
TFire1.Start
End Sub

Private Sub Form_Unload(Cancel As Integer)
TFire1.End
End Sub

Private Sub TFire1_ThreadEvent(ByVal strEvent As String)
MsgBox strEvent
End Sub

调试运行,就可以了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值