| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // A utility class that makes it easy to register for registry change |
| // notifications. |
| // |
| |
| #include "chrome_frame/registry_watcher.h" |
| |
| #include "chrome_frame/chrome_frame_helper_util.h" |
| |
| namespace { |
| const wchar_t kRegistryWatcherEventName[] = L"chrome_registry_watcher_event"; |
| } // namespace |
| |
| RegistryWatcher::RegistryWatcher(HKEY hive, |
| const wchar_t* path, |
| NotifyFunc callback) |
| : callback_(callback), |
| wait_event_(NULL), |
| wait_handle_(NULL), |
| stopping_(false) { |
| // Enforce that we can open the given registry path with the KEY_NOTIFY |
| // permission. |
| LONG result = RegOpenKeyEx(hive, path, 0, KEY_NOTIFY, ®istry_key_); |
| if (result != ERROR_SUCCESS) { |
| registry_key_ = NULL; |
| } |
| } |
| |
| RegistryWatcher::~RegistryWatcher() { |
| StopWatching(); |
| if (registry_key_) { |
| RegCloseKey(registry_key_); |
| registry_key_ = NULL; |
| } |
| } |
| |
| bool RegistryWatcher::StartWatching() { |
| if (!registry_key_ || wait_event_ || !callback_) { |
| return false; |
| } |
| |
| bool result = false; |
| wait_event_ = CreateEvent(NULL, |
| FALSE, // Auto-resets |
| FALSE, // Initially non-signalled |
| kRegistryWatcherEventName); |
| if (wait_event_ != NULL) { |
| LONG notify_result = RegNotifyChangeKeyValue( |
| registry_key_, |
| TRUE, // Watch subtree |
| REG_NOTIFY_CHANGE_NAME, // Notifies if a subkey is added. |
| wait_event_, |
| TRUE); // Asynchronous, signal the event when a change occurs. |
| |
| if (notify_result == ERROR_SUCCESS) { |
| if (RegisterWaitForSingleObject(&wait_handle_, |
| wait_event_, |
| &RegistryWatcher::WaitCallback, |
| reinterpret_cast<void*>(this), |
| INFINITE, |
| WT_EXECUTEDEFAULT)) { |
| stopping_ = false; |
| result = true; |
| } |
| } |
| } |
| |
| // If we're not good to go we don't need to hold onto the event. |
| if (!result && wait_event_) { |
| CloseHandle(wait_event_); |
| wait_event_ = NULL; |
| } |
| |
| return result; |
| } |
| |
| void RegistryWatcher::StopWatching() { |
| stopping_ = true; |
| if (wait_handle_) { |
| // Unregister the wait and block until any current handlers have returned. |
| UnregisterWaitEx(wait_handle_, INVALID_HANDLE_VALUE); |
| wait_handle_ = NULL; |
| } |
| if (wait_event_) { |
| CloseHandle(wait_event_); |
| wait_event_ = NULL; |
| } |
| } |
| |
| void CALLBACK RegistryWatcher::WaitCallback(void* param, BOOLEAN wait_fired) { |
| RegistryWatcher* watcher = reinterpret_cast<RegistryWatcher*>(param); |
| if (watcher->stopping_) |
| return; |
| |
| if (watcher->callback_) { |
| watcher->callback_(); |
| } |
| } |