This source file includes following definitions.
- enumerating_
- Initialize
- OnTick
- CheckChildWindow
- ChildWndEnumProc
#include "chrome/browser/hang_monitor/hung_window_detector.h"
#include <windows.h>
#include <atlbase.h>
#include "base/logging.h"
#include "chrome/browser/hang_monitor/hang_crash_dump_win.h"
#include "content/public/common/result_codes.h"
namespace {
static const int kTerminateTimeout = 2000;
}
const wchar_t HungWindowDetector::kHungChildWindowTimeout[] =
L"Chrome_HungChildWindowTimeout";
HungWindowDetector::HungWindowDetector(HungWindowNotification* notification)
: notification_(notification),
top_level_window_(NULL),
message_response_timeout_(0),
enumerating_(false) {
DCHECK(NULL != notification_);
}
HungWindowDetector::~HungWindowDetector() {
}
bool HungWindowDetector::Initialize(HWND top_level_window,
int message_response_timeout) {
if (NULL == notification_) {
return false;
}
if (NULL == top_level_window) {
return false;
}
top_level_window_ = top_level_window;
message_response_timeout_ = message_response_timeout;
return true;
}
void HungWindowDetector::OnTick() {
do {
base::AutoLock lock(hang_detection_lock_);
if (enumerating_) {
return;
}
enumerating_ = true;
} while (false);
EnumChildWindows(top_level_window_, ChildWndEnumProc,
reinterpret_cast<LPARAM>(this));
if (!::IsWindowEnabled(top_level_window_) &&
!::GetWindow(top_level_window_, GW_ENABLEDPOPUP)) {
::EnableWindow(top_level_window_, TRUE);
}
enumerating_ = false;
}
bool HungWindowDetector::CheckChildWindow(HWND child_window) {
if (!IsWindow(child_window)) {
return true;
}
DWORD top_level_window_thread_id =
GetWindowThreadProcessId(top_level_window_, NULL);
DWORD child_window_process_id = 0;
DWORD child_window_thread_id =
GetWindowThreadProcessId(child_window, &child_window_process_id);
bool continue_hang_detection = true;
if (top_level_window_thread_id != child_window_thread_id) {
#pragma warning(disable:4311)
int child_window_message_timeout =
reinterpret_cast<int>(GetProp(child_window, kHungChildWindowTimeout));
#pragma warning(default:4311)
if (!child_window_message_timeout) {
child_window_message_timeout = message_response_timeout_;
}
DWORD_PTR result = 0;
if (0 == SendMessageTimeout(child_window,
WM_NULL,
0,
0,
SMTO_BLOCK,
child_window_message_timeout,
&result)) {
HungWindowNotification::ActionOnHungWindow action =
HungWindowNotification::HUNG_WINDOW_IGNORE;
#pragma warning(disable:4312)
SetProp(child_window, kHungChildWindowTimeout,
reinterpret_cast<HANDLE>(child_window_message_timeout));
#pragma warning(default:4312)
continue_hang_detection =
notification_->OnHungWindowDetected(child_window, top_level_window_,
&action);
if (!IsChild(top_level_window_, child_window)) {
return continue_hang_detection;
}
if (action == HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS) {
RemoveProp(child_window, kHungChildWindowTimeout);
CHandle child_process(OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
child_window_process_id));
if (NULL == child_process.m_h) {
return continue_hang_detection;
}
DWORD process_id_check = 0;
GetWindowThreadProcessId(child_window, &process_id_check);
if (process_id_check != child_window_process_id) {
return continue_hang_detection;
}
CrashDumpAndTerminateHungChildProcess(child_process);
child_process.Close();
}
} else {
RemoveProp(child_window, kHungChildWindowTimeout);
}
}
return continue_hang_detection;
}
BOOL CALLBACK HungWindowDetector::ChildWndEnumProc(HWND child_window,
LPARAM param) {
HungWindowDetector* detector_instance =
reinterpret_cast<HungWindowDetector*>(param);
if (NULL == detector_instance) {
NOTREACHED();
return FALSE;
}
return detector_instance->CheckChildWindow(child_window);
}