[英]Change GUI in thread
我有一個操作,大約20秒結束。 為了避免凍結,我想創建一個線程並每秒更新一個標簽文本。 我搜索了很多,因為每個人都有不同的意見,我無法決定使用哪種方法。
我嘗試使用SendMessage並且它有效,但有些人認為使用SendMessage並不安全,我應該使用PostMessage。 但是PostMessage因ERROR_MESSAGE_SYNC_ONLY
而失敗(1159)。
char text[20] = "test text";
SendMessage(label_hwnd, WM_SETTEXT, NULL, text);
我搜索了這個,我認為這是因為在PostMessage中使用指針是不允許的。 這就是它失敗的原因。
所以我該怎么做? 我糊塗了。 你有什么建議? 這種方法是否適合更改其他線程中的UI元素?
謝謝
好吧,錯誤說明了一切。 消息無法異步發送。 關於PostMessage
的事情是它將消息發布到偵聽線程的隊列並立即返回,而不等待消息處理的結果。 另一方面, SendMessage
等待窗口過程完成處理消息,然后才返回。
在您的情況下使用PostMessage
的風險是在窗口過程處理之前您可能已釋放字符串緩沖區的消息。 因此,在這個實例中使用SendMessage
更安全,這就是MS開發人員在決定不允許異步發布此特定消息時可能會想到的內容。
編輯:要明確,當然這並不能消除完全傳遞裸指針的風險。
ERROR_MESSAGE_SYNC_ONLY
的文檔說:
該消息只能用於同步操作。
這意味着您可以使用同步消息傳遞,即SendMessage
等,但您不能使用異步消息傳遞,即PostMessage
。
原因是WM_SETTEXT
是一個參數包含引用的消息。 參數不能按值復制。 如果您可以異步傳送WM_SETTEXT
那么系統如何保證收件人窗口收到的指針仍然有效?
因此,系統只是拒絕您發送此消息的嘗試,實際上拒絕任何其他具有參考參數的消息。
在這里使用SendMessage
是合理的。 這肯定會奏效。
但是,您強制您的工作線程在UI上阻止。 UI可能需要一些時間來更新標題的文本。 另一種方法是將自定義消息發布到UI線程,指示UI線程更新UI。 然后,您的工作線程線程可以繼續其任務並讓UI線程並行更新,而不會阻止工作線程。
為了使其工作,您需要一種方法讓UI線程從工作線程獲取進度信息。 如果進度像百分比一樣簡單,那么您需要做的就是讓工作線程寫入,並從共享變量讀取UI線程。
asynch PostMessage()替代方案要求參數中傳遞的數據的生命周期超出消息發起方函數。 這樣做的“經典”方法是堆分配數據,PostMessage是指向它的指針,以通常的方式處理消息處理程序中的數據,然后將其刪除,(或以其他方式處理它)不泄漏)。 換句話說,“發射並忘記” - 在發布PostMessage后,您不得觸摸原始線程中的數據。
好處是PostMessage()允許原始線程“立即”運行,因此可以進一步工作(可能發布更多消息)。 如果GUI忙,則會發送SendMessage()和此類同步通信,從而影響整體吞吐量。
缺點是線程可能比GUI可以處理它們更快地生成消息。 這通常表現為滯后的GUI響應,特別是在執行GUI-intenisve工作時,如移動/調整窗口大小和更新TreeViews。 最終,當10,000多條消息排隊時,PostMessage調用將失敗。 如果發現這是一個問題,可能必須添加額外的流量控制,因此進一步使通信復雜化(我通常通過使用固定大小的對象池來阻止/限制原始線程,如果所有可用對象都是在已過帳但未處理的郵件中卡住了“過境”。
來自MSDN
如果將WM_USER下面的消息發送到異步消息函數(PostMessage,SendNotifyMessage和SendMessageCallback),則其消息參數不能包含指針。 否則,操作將失敗。
我想你可以在這里安全地使用SendMessage。 然后,您不必擔心字符串和其他問題的內存持久性。 當您從另一個消息處理程序發送消息或向已阻止的GUI線程發送消息時,SendMessage不安全,但如果在您的情況下您知道它是安全的 - 只需使用它
這不是PostMessage
的問題,但是您發送的消息存在問題 - WM_SETTEXT
。 首先,一個常見的誤解是,如果SendMessage()來自一個線程的控件,它與調用GUI API不同,它實際上不是 。 當你調用GUI API(從任何地方)例如設置文本時,windows以SendMessage()調用的形式實現它。 因此,當您發送相同的消息時,它與調用API基本相同。 雖然像這樣的GUI直接訪問在很多方面都有用,但不建議這樣做。 出於這個原因,我不同意@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處理函數中的文本並刪除內存。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.