kicad/thirdparty/sentry-native/external/crashpad/util/win/session_end_watcher.cc

230 lines
7.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2017 The Crashpad Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/win/session_end_watcher.h"
#include "base/logging.h"
#include "base/scoped_generic.h"
#include "util/win/scoped_set_event.h"
extern "C" {
extern IMAGE_DOS_HEADER __ImageBase;
} // extern "C"
namespace crashpad {
namespace {
// ScopedWindowClass and ScopedWindow operate on ATOM* and HWND*, respectively,
// instead of ATOM and HWND, so that the actual storage can exist as a local
// variable or a member variable, and the scoper can be responsible for
// releasing things only if the actual storage hasnt been released and zeroed
// already by something else.
struct ScopedWindowClassTraits {
static ATOM* InvalidValue() { return nullptr; }
static void Free(ATOM* window_class) {
if (*window_class) {
if (!UnregisterClass(MAKEINTATOM(*window_class), 0)) {
PLOG(ERROR) << "UnregisterClass";
} else {
*window_class = 0;
}
}
}
};
using ScopedWindowClass = base::ScopedGeneric<ATOM*, ScopedWindowClassTraits>;
struct ScopedWindowTraits {
static HWND* InvalidValue() { return nullptr; }
static void Free(HWND* window) {
if (*window) {
if (!DestroyWindow(*window)) {
PLOG(ERROR) << "DestroyWindow";
} else {
*window = nullptr;
}
}
}
};
using ScopedWindow = base::ScopedGeneric<HWND*, ScopedWindowTraits>;
// GetWindowLongPtr()s return value doesnt unambiguously indicate whether it
// was successful, because 0 could either represent successful retrieval of the
// value 0, or failure. This wrapper is more convenient to use.
bool GetWindowLongPtrAndSuccess(HWND window, int index, LONG_PTR* value) {
SetLastError(ERROR_SUCCESS);
*value = GetWindowLongPtr(window, index);
return *value || GetLastError() == ERROR_SUCCESS;
}
// SetWindowLongPtr() has the same problem as GetWindowLongPtr(). Use this
// wrapper instead.
bool SetWindowLongPtrAndGetSuccess(HWND window, int index, LONG_PTR value) {
SetLastError(ERROR_SUCCESS);
LONG_PTR previous = SetWindowLongPtr(window, index, value);
return previous || GetLastError() == ERROR_SUCCESS;
}
} // namespace
SessionEndWatcher::SessionEndWatcher()
: Thread(),
window_(nullptr),
started_(nullptr),
stopped_(nullptr) {
// Set bManualReset for these events so that WaitForStart() and WaitForStop()
// can be called multiple times.
started_.reset(CreateEvent(nullptr, true, false, nullptr));
PLOG_IF(ERROR, !started_.get()) << "CreateEvent";
stopped_.reset(CreateEvent(nullptr, true, false, nullptr));
PLOG_IF(ERROR, !stopped_.get()) << "CreateEvent";
Start();
}
SessionEndWatcher::~SessionEndWatcher() {
// Tear everything down by posting a WM_CLOSE to the window. This obviously
// cant work until the window has been created, and that happens on a
// different thread, so wait for the start event to be signaled first.
WaitForStart();
if (window_) {
if (!PostMessage(window_, WM_CLOSE, 0, 0)) {
PLOG(ERROR) << "PostMessage";
}
}
Join();
DCHECK(!window_);
}
void SessionEndWatcher::WaitForStart() {
if (WaitForSingleObject(started_.get(), INFINITE) != WAIT_OBJECT_0) {
PLOG(ERROR) << "WaitForSingleObject";
}
}
void SessionEndWatcher::WaitForStop() {
if (WaitForSingleObject(stopped_.get(), INFINITE) != WAIT_OBJECT_0) {
PLOG(ERROR) << "WaitForSingleObject";
}
}
void SessionEndWatcher::ThreadMain() {
ATOM atom = 0;
ScopedWindowClass window_class(&atom);
ScopedWindow window(&window_);
ScopedSetEvent call_set_stop(stopped_.get());
{
ScopedSetEvent call_set_start(started_.get());
WNDCLASS wndclass = {};
wndclass.lpfnWndProc = WindowProc;
wndclass.hInstance = reinterpret_cast<HMODULE>(&__ImageBase);
wndclass.lpszClassName = L"crashpad_SessionEndWatcher";
atom = RegisterClass(&wndclass);
if (!atom) {
PLOG(ERROR) << "RegisterClass";
return;
}
window_ = CreateWindow(MAKEINTATOM(atom), // lpClassName
nullptr, // lpWindowName
0, // dwStyle
0, // x
0, // y
0, // nWidth
0, // nHeight
nullptr, // hWndParent
nullptr, // hMenu
nullptr, // hInstance
this); // lpParam
if (!window_) {
PLOG(ERROR) << "CreateWindow";
return;
}
}
MSG message;
BOOL rv = 0;
while (window_ && (rv = GetMessage(&message, window_, 0, 0)) > 0) {
TranslateMessage(&message);
DispatchMessage(&message);
}
if (window_ && rv == -1) {
PLOG(ERROR) << "GetMessage";
return;
}
}
// static
LRESULT CALLBACK SessionEndWatcher::WindowProc(HWND window,
UINT message,
WPARAM w_param,
LPARAM l_param) {
// Figure out which object this is. A pointer to it is stuffed into the last
// parameter of CreateWindow(), which shows up as CREATESTRUCT::lpCreateParams
// in a WM_CREATE message. That should be processed before any of the other
// messages of interest to this function. Once the object is known, save a
// pointer to it in the GWLP_USERDATA slot for later retrieval when processing
// other messages.
SessionEndWatcher* self;
if (!GetWindowLongPtrAndSuccess(
window, GWLP_USERDATA, reinterpret_cast<LONG_PTR*>(&self))) {
PLOG(ERROR) << "GetWindowLongPtr";
}
if (!self && message == WM_CREATE) {
CREATESTRUCT* create = reinterpret_cast<CREATESTRUCT*>(l_param);
self = reinterpret_cast<SessionEndWatcher*>(create->lpCreateParams);
if (!SetWindowLongPtrAndGetSuccess(
window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self))) {
PLOG(ERROR) << "SetWindowLongPtr";
}
}
if (self) {
if (message == WM_ENDSESSION) {
// If w_param is false, this WM_ENDSESSION message cancels a previous
// WM_QUERYENDSESSION.
if (w_param) {
self->SessionEnding();
// If the session is ending, post a close message which will kick off
// window destruction and cause the message loop thread to terminate.
if (!PostMessage(self->window_, WM_CLOSE, 0, 0)) {
PLOG(ERROR) << "PostMessage";
}
}
} else if (message == WM_DESTROY) {
// The window is being destroyed. Clear GWLP_USERDATA so that |self| wont
// be found during a subsequent call into this function for this window.
// Clear self->window_ too, because it refers to an object that soon wont
// exist. That signals the message loop to stop processing messages.
if (!SetWindowLongPtrAndGetSuccess(window, GWLP_USERDATA, 0)) {
PLOG(ERROR) << "SetWindowLongPtr";
}
self->window_ = nullptr;
}
}
// If the message is WM_CLOSE, DefWindowProc() will call DestroyWindow(), and
// this function will be called again with a WM_DESTROY message.
return DefWindowProc(window, message, w_param, l_param);
}
} // namespace crashpad