Chinaunix首页 | 论坛 | 博客
  • 博客访问: 185437
  • 博文数量: 90
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2017-08-23 16:48
文章分类

全部博文(90)

文章存档

2015年(1)

2011年(21)

2010年(59)

2009年(9)

我的朋友

分类: C/C++

2010-07-14 09:55:47

下面是jiangsheng(蒋晟.MSMVP2004Jan)老大的精彩回贴: 
 
Knowledge  Base  Article  Q157437:  "Fireev.exe  Fires  Events  from  a  Second  Thread"   
 
Knowledge  Base  Article  Q196026:  "PRB:  Firing  Event  in  Second  Thread  Causes  IPF  or  GPF"   
 
Michael  Lindig's  ATL:  Firing  events  from  worker  threads  article  on  CodeGuru.com   
 
Knowledge  Base  Article  Q280512:  "SAMPLE:  ATLCPImplMT  Encapsulates  ATL  Event  Firing  Across  COM  Apartments"   
 
COM  FAQ  11:  HOWTO:  Post  messages  to  a  hidden  window  for  raising  events  from  an  Apartment-threaded  object  employing  worker  threads   

这是从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
调试运行,就可以了

 
阅读(1562) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~