2014-01-29 84 views
3

我有一個操作,大約20秒後結束。爲了避免凍結,我想創建一個線程並每秒更新一個標籤文本。我搜查了很多,因爲每個人都有不同的意見,我不能決定使用哪種方法。更改線程中的GUI

我試着SendMessage,它的工作原理,但有些人認爲使用SendMessage不安全,我應該使用PostMessage來代替。但PostMessage以ERROR_MESSAGE_SYNC_ONLY(1159)失敗。

char text[20] = "test text"; 
SendMessage(label_hwnd, WM_SETTEXT, NULL, text); 

我搜索了這個,我認爲這是因爲在PostMessage中使用指針是不允許的。這就是它失敗的原因。

那麼,我該怎麼辦?我很困惑。你有什麼建議?此方法是否適用於更改其他線程中的UI元素?

謝謝

+0

「有些人」提到了爲什麼它不安全? – SChepurin

回答

4

ERROR_MESSAGE_SYNC_ONLY文檔說:

消息只能與同步操作一起使用。

這意味着,可以使用同步的消息傳遞,即SendMessage和相似,但不能使用異步消息傳遞,即PostMessage

原因是WM_SETTEXT是其參數包含引用的消息。參數不能按值複製。如果您可以異步交付WM_SETTEXT,那麼系統如何保證收件人窗口收到的指針仍然有效?

因此,系統會拒絕您嘗試發送此消息,實際上是其他參數爲參考的消息。

在這裏使用SendMessage是合理的。那肯定會奏效。

但是,您迫使您的工作線程在UI上阻塞。它可能需要一些時間來更新標題的文本。另一種方法是將自定義消息發佈到UI線程,以指示UI線程更新UI。然後,您的工作線程線程可以繼續其任務,並讓UI線程並行更新,而不會阻塞工作線程。

爲了使其工作,您需要一種方法讓UI線程從工作線程獲取進度信息。如果進度和百分比一樣簡單,那麼你所需要做的就是讓工作線程寫入一個共享變量並從中讀取UI線程。

+0

因此,SendMessage可以更改UI元素。你說我強制UI線程來處理導致塊的消息,但我認爲這不是什麼大問題。對?順便說一下,如何使用PostMessage傳遞文本字符串(因爲您建議使用自定義消息)? –

+0

是的,在你的問題中編寫的'SendMessage'將起作用。雖然你應該將字符串聲明爲const char * text =「test text」或者'char text [] =「test text」'而不是你問題中的代碼。這存在不被空終止的風險。 –

+0

我不理解你的推理,因爲'SendMessageCallback'返回相同的錯誤代碼,並且顯然沒有問題將用戶緩衝區傳遞給該函數,因爲你可以在返回時正確地釋放它。那麼是什麼給了? – Mehrdad

1

我想你可以在這裏安全地使用SendMessage。那麼你不需要擔心你的字符串和其他問題的內存持久性。 SendMessage函數是不是安全的,當你從另一個消息處理程序發送郵件或者發送消息至阻塞GUI線程,但如果你的情況,你知道它是安全的 - 只是用它

4

那麼,錯誤說明了一切。該消息不能異步發送。關於PostMessage的一點是,它將消息發佈到偵聽線程的隊列並立即返回,而不等待消息處理的結果。另一方面,SendMessage,等待窗口過程完成處理消息並且只有它返回。

在你的情況下使用PostMessage的風險是,在窗口過程處理消息之前,你可能已經釋放了字符串緩衝區。因此,在這種情況下使用SendMessage會更安全,這就是MS開發人員在決定不允許異步發送此特定消息時可能想到的內容。

編輯:只是要清楚,當然這並不能消除完全通過裸指針的風險。

+0

那麼,你是否建議使用SendMessage來更新UI元素?它可以更新文本框文本或更改進度條值。 –

3

MSDN

如果在範圍內發送消息下面WM_USER到異步消息功能(PostMessage的,SendNotifyMessage,和SendMessageCallback),其消息參數不能包括指針。否則,操作將失敗。

3

異步PostMessage()替代方法要求在參數中傳遞的數據的生存期超出了消息始發函數的範圍。這種做法的'經典'方法是堆分配數據,PostMessage指向它,以通常的方式處理消息處理程序中的數據,然後刪除它(或以其他方式處理它,不泄漏)。換句話說,「發射並忘記」 - PostMessage發佈後,您不能觸摸原始線程中的數據。

好處是PostMessage()允許原始線程'立即'運行,所以還有更多的工作(可能發佈更多的消息)。如果圖形用戶界面繁忙,則SendMessage()和此類同步通信可能會暫停,從而影響整體吞吐量。

缺點是線程可能會產生比GUI更快的消息。這通常表現爲laggy GUI響應,尤其是在執行GUI-intenisve工作時,例如移動/調整窗口大小和更新TreeView。最終,當超過10,000條消息排隊時,PostMessage調用將失敗。如果發現這是一個問題,則可能需要添加額外的流量控制,這樣會使通信更加複雜(我通常通過使用固定大小的對象池來阻止/限制始發線程,如果所有可用對象都是在發佈但未處理的郵件中被卡在「傳輸中」

+0

謝謝你的評論,先生。這非常有幫助。 –

1

這不是PostMessage的問題,而是與您要發送的郵件有關的問題 - 第一個常見的誤解是,如果將SendMessage()控制從一個線程,它不同於調用GUI API,它實際上是不是當你調用一個GUI API(從任何地方)例如設置文本時,windows以SendMessage()的形式實現這個調用。當你發送相同的消息時,它和調用API基本相同,儘管直接的GUI訪問像th在很多方面有效,不建議使用。出於這個原因,我會乞求不同意@David接受的答案。

正確的方法是(飛碼)

char* text = new char[20] 
strcpy_s(text, "test text"); 
PostMessage(label_hwnd, IDM_MY_MSG_UPDATE_TEXT, NULL, text); 

你會更新自己的消息IDM_MY_MSG_UPDATE_TEXT處理函數的文本和刪除的記憶。

+0

感謝您的評論。你說:「雖然直接GUI訪問像這樣在很多方面起作用,但不建議這樣做」。你能告訴我爲什麼不推薦?謝謝 –

+0

@MalikÇelik這是一個很大的問題,但通常它取決於你在做什麼..如果只有一個線程訪問GUI,它不會與GUI或其他線程相沖突。然而,當你沒有線程同步的時候,忽視某些東西的風險就在那裏。 – zar

+0

啊哈,所以你說的是一般線程問題。即使是全局整數,此問題也是有效的。線程應該謹慎訪問它。謝謝你zadane –