Hiện tượng chương trình bị treo khi gọi hàm FindWindow trong luồng trên WinCE7.0

Gặp phải sự cố bất ngờ khiến ứng dụng bị treo hoàn toàn!

Ứng dụng này hoạt động trong môi trường hệ thống lớn, không có bất kỳ cập nhật nào từ phía chúng tôi. Tuy nhiên, theo quan sát, hiện tượng treo xảy ra sau khi ứng dụng xử lý tin nhắn được gửi từ giao diện người dùng (UI) qua hàm SendMessage. Khi chạy phiên bản cũ của ứng dụng, lỗi này không xuất hiện.

Dù ban đầu nghi ngờ không phải do lỗi từ phía ứng dụng, nhưng sau khi kiểm tra kỹ, nguyên nhân được xác định là do hàm FindWindow không trả về giá trị, gây treo toàn bộ luồng. Qua việc thêm nhiều log, phát hiện thấy dòng "start call FindWindow" được ghi nhận nhưng không có dòng "end of call FindWindow" nào xuất hiện.

Xem xét lại đoạn mã, phát hiện ra trước đây đã từng gặp vấn đề tương tự. Một dòng FindWindow được dùng để kiểm tra và bị chú thích, nhưng không được xóa hoàn toàn. Mã nguồn minh họa như sau:

1 LOG_MSG("start call FindWindow.\r\n");  
2 // hWnd = ::FindWindow(NULL, L"TestAppWindow");  // Gây treo luồng khi gọi trong thread  
3 hWnd = ::FindWindow(NULL, L"Test2AppWindow");  
4 LOG_MSG("end of call FindWindow.\r\n");  

Nguyên nhân gây treo chính là dòng: hWnd = ::FindWindow(NULL, L"Test2AppWindow");

Sử dụng công cụ Remote Spy để kiểm tra thông tin tiến trình trước và sau khi xảy ra lỗi, phát hiện tên cửa sổ mục tiêu (Test2AppWindow) bị thay bằng chuỗi rỗng. Tuy nhiên, khi dùng Remote Process Viewer, tên cửa sổ vẫn hiển thị đúng là Test2AppWindow.

Sau khi lỗi xảy ra, viết lại chương trình đơn giản để kiểm tra, thấy rằng FindWindow có thể tìm thấy cửa sổ đúng như mong đợi.

Dưới đây là các hình ảnh chụp từ công cụ hỗ trợ:

  1. Trường hợp hoạt động bình thường:

  2. Trường hợp xảy ra lỗi:

Sự khác biệt lớn nhất nằm ở phần "Window Property" với nội dung "Window" được hiển thị.

Mặc dù đã xác định một phần nguyên nhân, vẫn cần tiếp tục phân tích sâu hơn.

Kết quả phân tích tiếp theo như sau:

/*
 * Cùng một lệnh FindWindow(NULL, L"TestFindWindow");
 * Khi thực thi trong hàm xử lý phím bấm, thời gian khoảng: 5ms
 * Khi thực thi trong luồng, do hàm Sleep trong phím bấm, thời gian khoảng: 10020ms
 * Kết quả trên nền Win32 và MFC là như nhau
 *
 * Log thực thi sau khi nhấn phím:
 starttest FindWindow: CSmartDeviceMFCDlg::OnBnClickedButton2
 end of test FindWindow:CSmartDeviceMFCDlg::OnBnClickedButton2,tick: 5
 start test FindWindow:TestFindWindowThreadProc
 Sleep 0
 Sleep 1
 Sleep 2
 Sleep 3
 Sleep 4
 Sleep 5
 Sleep 6
 Sleep 7
 Sleep 8
 Sleep 9
 end of test FindWindow:TestFindWindowThreadProc(10020)
 Luồng 'TestFindWindowThreadProc' (0x45e0336) đã kết thúc, giá trị trả về là 0 (0x0).
*/
 1 DWORD WINAPI TestFindWindowThreadProc(void * pParam) // Luồng tìm kiếm handle cửa sổ  
 2 {  
 3     DWORD dwStartTime = GetTickCount();  
 4     printf("\r\nstart test FindWindow:%s\r\n",__FUNCTION__);  
 5     // Gọi FindWindow trong luồng, nhưng thực tế hành động được thực hiện qua SendMessage đến tiến trình cửa sổ (nếu cửa sổ chính bị chặn ???)  
 6     // Theo đoạn mã kiểm tra này, Sleep trong hàm xử lý phím bấm đã chặn việc thực thi FindWindow; chỉ khi Sleep kết thúc thì FindWindow mới được thực hiện  
 7     FindWindow(NULL, L"TestFindWindow");  
 8     printf("\r\nend of test FindWindow:%s(%d)\r\n",__FUNCTION__,GetTickCount() - dwStartTime);  
 9    
10     return 0;  
11 }  
12    
13 void CSmartDeviceMFCDlg::OnBnClickedButton2()  
14 {  
15     // TODO: Thêm mã xử lý cho sự kiện nút bấm  
16     DWORD dwStartTime = GetTickCount();  
17     printf("\r\nstart test FindWindow:%s\r\n",__FUNCTION__);  
18     FindWindow(NULL, L"TestFindWindow");  
19     printf("\r\nend of test FindWindow:%s,tick: %d\r\n",__FUNCTION__,GetTickCount() - dwStartTime);  
20     CloseHandle(CreateThread(NULL,0,TestFindWindowThreadProc,NULL,0,0));  
21    
22     for(int i = 0; i < 10; i++)  
23     {  
24            Sleep(1000);  
25            printf("Sleep %d\r\n",i);  
26     }  
27 }  

Thẻ: WinCE7.0 FindWindow multithreading deadlock Windows API

Đăng vào ngày 15 tháng 6 lúc 01:56