zoukankan      html  css  js  c++  java
  • VC在线程中操作界面

     http://blog.csdn.net/tingsking18/article/details/4399199

           多线程是我们在编程中经常遇到的问题,线程执行完后往往要把执行的结果传给主线程,但是MFC的控件不是线程安全的,所以在线程中操作界面是一件很危险的事情。所以就需要安全的方法。这一系列篇文章我将介绍VC在线程操作界面方法。

    问题描述:

    1.界面上有两个Button m_btn1,m_btn2。m_btn创建一个线程,来操作m_btn2。

    void CMyDlg::OnButton1()

    {

           _beginthread(ThreadStart,0,(void *)(m_btn2));

    }

    2.线程执行的代码如下:

    void ThreadStart(void* p)

    {

           CButton *btn = (CButton*)(p);

           btn->SetWindowText("aaaaaaa");

    }

    这是我们想象的代码,但是如果这样写来,是不正确的。是因为MFC窗体程序的界面控件是由主线程来控制的,由于我在新创建的线程中也操作了界面的控件,这样就同时由两个线程操作一个控件。但是这两个线程又没有进行同步。所以就发生了错误。

    下面我们就通过让工作线程和主线程进行同步来解决上面的问题。

    1.定义MYMSG

    #define MYMSG WM_USER +200

    2.线程启动的代码如下:

    void CMyDlg::OnButton1()

    {

           _beginthread(ThreadStart,0,(void *)(this));

    }

    3.线程执行的代码如下:

    void ThreadStart(void* p)

    {

           CString str = "aaaaaa";

           CMyDlg *btn = (CMyDlg*)(p);

    PostMessage(btn->m_hWnd,MYMSG,WPARAM(&str),NULL);

           Sleep(1000);

    }

    4.添加消息映射

    ON_MESSAGE(MYMSG,OnMsgBack)

    5.添加消息映射函数的定义和实现

    LRESULT OnMsgBack(WPARAM wParam,LPARAM lParam);

    LRESULT CMyDlg::OnMsgBack(WPARAM wParam,LPARAM lParam)

    {

    CString* str = (CString*)wParam;

    m_btn2.SetWindowText(*str);

    return 0;

    }

    这样就完成了在线程中操作界面的工作。

    注意:

    1. 我们也可以使用继承CwinThread的类,然后在消息映射中用

    ON_THREAD_MESSAGE来处理往线程中发送的消息。

    2.  在线程中往主线程PostMessage发送数据的时候,一定要保证数据在主线程使用之前是存在的。Sleep(1000);是为了保证让主线程先处理到str。否则,执行完ThreadStart函数后,str自动销毁了,然后在外面访问这个已经销毁的指针是非常危险的。

    我们可以通过另外一种方法来解决。

    1. 定义消息处理函数和SetWindowLong返回值

    static LRESULT CALLBACK PluginWinProc(HWND, UINT, WPARAM, LPARAM);

    long   OldProc;

    1. 在Button1的处理函数中创建线程

    void CMyDlg::OnButton1()

    {

    HWND h = m_btn2.m_hWnd;

    OldProc = SetWindowLong(h,GWL_WNDPROC,long(PluginWinProc));

    _beginthread(ThreadStart1,0,(void *)(&m_btn2));

    }

    3.

    void CMyDlg::OnDestroy()

    {

    CDialog::OnDestroy();

    SetWindowLong(m_btn2.m_hWnd,GWL_WNDPROC,OldProc);                  

    }

    4.

    static LRESULT CALLBACK PluginWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

    {

        switch (msg)

        {

        case MYMSG:

            {

                CWnd *wnd = CWnd::FromHandle(hwnd);

                wnd->SetWindowText("aaaaaaa");

            }

            break;

        default:

            break;

        }

        return   CallWindowProc((WNDPROC)OldProc,hwnd,msg,wParam,lParam); 

    }

    代码都非常简单。关键的是SetWindowLong这个函数,这个函数的作用是设定窗口属性。GWL_WNDPROC 为窗口过程设置新地址。下面是MSDN中关于SetWindowLong和GWL_WNDPROC的说明:

    若使用SetWindowLong函数和GWL_WNDPROC索引替换窗口过程,则给定的窗口过程必须遵

    循WindowProc回调函数的说明中指定的准则。

    使用GWL_WNDPROC索引调用SetWindowLong函数可创建该窗口类的子类(窗口类用来创建窗

    口)。应用程序不得用另一个过程的窗口产生子类。

    当然要在使用完后在用SetWindowLong原来的窗口过程设置回去。使用这种方法可用于修改你无法更改代码的类,可以重写他的消息处理函数。

  • 相关阅读:
    四、面向对象分析和设计全流程概述
    三、三大核心特征-继承
    二、三大核心特征-多态
    [第三章]一、三大核心特征-封装
    四、抽象类
    三、接口
    二、对象
    [第二章]一、类
    六、面向对象的迷思
    五、面向对象的应用范围
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/4046749.html
Copyright © 2011-2022 走看看