VC線上程中操作介面

Max Woods發表於2014-10-23

 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原來的視窗過程設定回去。使用這種方法可用於修改你無法更改程式碼的類,可以重寫他的訊息處理函式。

相關文章