2012-12-18 22 views
12

Làm thế nào tôi có thể triển khai một vòng lặp tin nhắn trong một chuỗi bằng cách sử dụng OTL? Application.ProcessMessages; là những gì tôi sử dụng cho đến nay nhưng nó không phải là rất an toàn để sử dụng nó.MessageLoop trong Chủ đề

Cảm ơn

Trả lời

17

Đây là cách tôi kéo thông điệp tắt hàng đợi của một chủ đề:

while GetMessage(Msg, 0, 0, 0) and not Terminated do begin 
    Try 
    TranslateMessage(Msg); 
    DispatchMessage(Msg); 
    Except 
    Application.HandleException(Self); 
    End; 
end; 

Sử dụng Application.ProcessMessages sẽ kéo thông điệp của các hàng đợi của tiểu trình đang gọi. Nhưng nó không thích hợp để sử dụng trong một vòng lặp tin nhắn bởi vì nó sẽ không chặn. Đó là lý do tại sao bạn sử dụng GetMessage. Nó chặn nếu hàng đợi trống. Và Application.ProcessMessages cũng gọi các phương thức khác TApplication không được thiết kế để đảm bảo an toàn cho luồng. Vì vậy, có rất nhiều lý do để không gọi nó từ một chủ đề khác với chủ đề chính.

Khi bạn cần phải chấm dứt các chủ đề, sau đó tôi làm điều này:

Terminate; 
PostThreadMessage(ThreadID, WM_NULL, 0, 0); 
//wake the thread so that it can notice that it has terminated 

Không ai trong số này là OTL cụ thể. Mã này tất cả được thiết kế để sống trong một hậu duệ TThread. Tuy nhiên, ý tưởng có thể được chuyển nhượng.


Trong nhận xét bạn chỉ ra rằng bạn muốn chạy vòng lặp thông báo bận, không chặn. Bạn sẽ sử dụng PeekMessage cho điều đó.

while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin 
    Try 
    TranslateMessage(Msg); 
    DispatchMessage(Msg); 
    Except 
    Application.HandleException(Self); 
    End; 
end; 
+0

Điều này có vẻ rất phức tạp trên bề mặt. Nó có thể được đặt trong một thủ tục? Như thế này: "trong khi IHTMLDocument2.readyState <> 'complete' làm MessageLoop;" –

+0

Đó sẽ là một vòng lặp bận, hơi khác. Bản cập nhật của tôi cho thấy cách thực hiện điều đó với 'PeekMessage'. Trong một thế giới lý tưởng, bạn sẽ không làm điều đó. Các vòng bận rộn tiêu thụ chu kỳ CPU không cần thiết. Trong một thế giới lý tưởng, bạn sẽ nhận được thông báo rằng tài liệu của bạn đã sẵn sàng. –

+0

@DavidHeffernan Có vẻ như 'trong khi GetMessage' không phải là cách được khuyến nghị? https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx – SOUser

3

Bạn có thể tạo vòng lặp tương tự như trong Application.Run.

Bạn có thể gọi PeekMessage, kiểm tra xem có tin nhắn không. PeekMessage kiểm tra hàng đợi thư của chủ đề hiện tại, vì vậy nếu bạn sử dụng nó trong chuỗi của mình, nó sẽ kiểm tra hàng đợi thư của chuỗi (để bạn có thể đăng bài viết bằng cách sử dụng PostThreadMessage).

Thay vì PeekMessage, bạn cũng có thể sử dụng GetMessage, đợi cho đến khi nhận được tin nhắn. GetMessage trả về 0 trả về false. *)

khi nhận được thông báo WM_QUIT, đây cũng là tín hiệu để kết thúc vòng lặp tin nhắn. Sẽ rất nguy hiểm khi gọi lại GetMessage sau đó, vì nó có thể không nhận được một tin nhắn khác và nó có thể ngăn ứng dụng của bạn đóng cửa đúng cách, vì nó đang chặn.

Application.ProcessMessages thực sự không phải là rất an toàn, bởi vì nó thực hiện rất nhiều tính năng bổ sung dành riêng cho chuỗi chính. Đối với một, nó kích hoạt Application.OnIdle ngay khi hàng đợi thông báo trống, điều đó có nghĩa là nó gọi (vấn đề 1) khi hàng đợi thông điệp luồng trống và (vấn đề 2) trong ngữ cảnh của luồng, không cho phép bất kỳ VCL tương tác trong sự kiện này.

* ) Giới thiệu về giá trị trả về của GetMessage: Tôi nhận thấy Delphi thực hiện GetMessage là trả về LongBool. Tuy nhiên giá trị trả về thực tế là một số nguyên. Nó trả về 0 trong trường hợp WM_QUIT, -1 trong trường hợp có lỗi và khác không trong các trường hợp khác. Microsoft states:

Bởi vì giá trị trả về có thể khác không, không, hoặc -1, tránh mã như này:

while (GetMessage(lpMsg, hWnd, 0, 0)) ... 

Thật không may, bạn sẽ phải nhập khẩu GetMessage dưới một bí danh khác nhau để sử dụng nó một cách chính xác.

+0

Thay vì nhập lại, bạn chỉ có thể nhập kết quả của GetMessage. –