1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/win/message_window.h"
#include <windows.h>
#include <map>
#include <utility>
#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ref.h"
#include "base/no_destructor.h"
#include "base/process/memory.h"
#include "base/strings/string_util.h"
#include "base/thread_annotations.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_local.h"
#include "base/win/current_module.h"
#include "base/win/resource_exhaustion.h"
#include "base/win/wrapped_window_proc.h"
// To avoid conflicts with the macro from the Windows SDK...
#undef FindWindow
const wchar_t kMessageWindowClassName[] = L"Chrome_MessageWindow";
namespace {
// This class can be accessed from multiple threads,
// this is handled by each thread having a different instance.
class MessageWindowMap {
public:
static MessageWindowMap& GetInstanceForCurrentThread() {
static base::NoDestructor<base::ThreadLocalOwnedPointer<MessageWindowMap>>
instance;
if (!instance->Get()) {
instance->Set(base::WrapUnique(new MessageWindowMap));
}
return *(instance->Get());
}
MessageWindowMap(const MessageWindowMap&) = delete;
MessageWindowMap& operator=(const MessageWindowMap&) = delete;
// Each key should only be inserted once.
void Insert(HWND hwnd, base::win::MessageWindow& message_window) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
CHECK(map_.emplace(hwnd, message_window).second);
}
// Erase should only be called on an existing key.
void Erase(HWND hwnd) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Check that exactly one element is erased from the map.
CHECK_EQ(map_.erase(hwnd), 1u);
}
// Will return nullptr if `hwnd` is not in the map.
base::win::MessageWindow* Get(HWND hwnd) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (auto search = map_.find(hwnd); search != map_.end()) {
return &(search->second.get());
}
return nullptr;
}
private:
MessageWindowMap() = default;
THREAD_CHECKER(thread_checker_);
std::map<HWND, const raw_ref<base::win::MessageWindow>> map_
GUARDED_BY_CONTEXT(thread_checker_);
};
} // namespace
namespace base {
namespace win {
// Used along with LazyInstance to register a window class for message-only
// windows created by MessageWindow.
class MessageWindow::WindowClass {
public:
WindowClass();
WindowClass(const WindowClass&) = delete;
WindowClass& operator=(const WindowClass&) = delete;
~WindowClass();
ATOM atom() { return atom_; }
HINSTANCE instance() { return instance_; }
private:
ATOM atom_ = 0;
HINSTANCE instance_ = CURRENT_MODULE();
};
static LazyInstance<MessageWindow::WindowClass>::DestructorAtExit
g_window_class = LAZY_INSTANCE_INITIALIZER;
MessageWindow::WindowClass::WindowClass() {
WNDCLASSEX window_class;
window_class.cbSize = sizeof(window_class);
window_class.style = 0;
window_class.lpfnWndProc = &WrappedWindowProc<WindowProc>;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = instance_;
window_class.hIcon = nullptr;
window_class.hCursor = nullptr;
window_class.hbrBackground = nullptr;
window_class.lpszMenuName = nullptr;
window_class.lpszClassName = kMessageWindowClassName;
window_class.hIconSm = nullptr;
atom_ = RegisterClassEx(&window_class);
if (atom_ == 0) {
PLOG(ERROR)
<< "Failed to register the window class for a message-only window";
OnResourceExhausted();
}
}
MessageWindow::WindowClass::~WindowClass() {
if (atom_ != 0) {
BOOL result = UnregisterClass(MAKEINTATOM(atom_), instance_);
// Hitting this DCHECK usually means that some MessageWindow objects were
// leaked. For example not calling
// ui::Clipboard::DestroyClipboardForCurrentThread() results in a leaked
// MessageWindow.
DCHECK(result);
}
}
MessageWindow::MessageWindow() = default;
MessageWindow::~MessageWindow() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (window_ != nullptr) {
BOOL result = DestroyWindow(window_);
DCHECK(result);
}
}
bool MessageWindow::Create(MessageCallback message_callback) {
return DoCreate(std::move(message_callback), nullptr);
}
bool MessageWindow::CreateNamed(MessageCallback message_callback,
const std::wstring& window_name) {
return DoCreate(std::move(message_callback), window_name.c_str());
}
// static
HWND MessageWindow::FindWindow(const std::wstring& window_name) {
return FindWindowEx(HWND_MESSAGE, nullptr, kMessageWindowClassName,
window_name.c_str());
}
bool MessageWindow::DoCreate(MessageCallback message_callback,
const wchar_t* window_name) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(message_callback_.is_null());
DCHECK(!window_);
message_callback_ = std::move(message_callback);
WindowClass& window_class = g_window_class.Get();
window_ =
CreateWindow(MAKEINTATOM(window_class.atom()), window_name, 0, 0, 0, 0, 0,
HWND_MESSAGE, nullptr, window_class.instance(), this);
if (!window_) {
if (::GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
base::TerminateBecauseOutOfMemory(0);
}
PLOG(ERROR) << "Failed to create a message-only window";
return false;
}
return true;
}
// static
LRESULT CALLBACK MessageWindow::WindowProc(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam) {
// This can be called from different threads for different windows,
// each thread has its own MessageWindowMap instance.
auto& message_window_map = MessageWindowMap::GetInstanceForCurrentThread();
MessageWindow* self = message_window_map.Get(hwnd);
// CreateWindow will send a WM_CREATE message during window creation.
if (!self && message == WM_CREATE) [[unlikely]] {
CREATESTRUCT* const cs = reinterpret_cast<CREATESTRUCT*>(lparam);
self = reinterpret_cast<MessageWindow*>(cs->lpCreateParams);
// Tell the MessageWindow instance the HWND that CreateWindow has produced.
self->window_ = hwnd;
// Associate the MessageWindow instance with the HWND in the map.
message_window_map.Insert(hwnd, *self);
}
if (!self) [[unlikely]] {
return DefWindowProc(hwnd, message, wparam, lparam);
}
LRESULT message_result = {};
if (!self->message_callback_.Run(message, wparam, lparam, &message_result)) {
message_result = DefWindowProc(hwnd, message, wparam, lparam);
}
if (message == WM_DESTROY) [[unlikely]] {
// Tell the MessageWindow instance that it no longer has an HWND.
self->window_ = nullptr;
// Remove this HWND's MessageWindow from the map since it is going away.
message_window_map.Erase(hwnd);
}
return message_result;
}
} // namespace win
} // namespace base
|