簡體   English   中英

在線程中更改GUI

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM