diff --git a/src/win32/Com.hxx b/src/win32/Com.hxx new file mode 100644 index 000000000..cfc85f47e --- /dev/null +++ b/src/win32/Com.hxx @@ -0,0 +1,45 @@ +/* + * Copyright 2020 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_WIN32_COM_HXX +#define MPD_WIN32_COM_HXX + +#include "HResult.hxx" +#include +#include + +// RAII for Microsoft Component Object Model(COM) +// https://docs.microsoft.com/en-us/windows/win32/api/_com/ +class COM { +public: + COM() { + HRESULT result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (FAILED(result)) { + throw FormatHResultError(result, "Unable to initialize COM"); + } + } + ~COM() noexcept { CoUninitialize(); } + + COM(const COM &) = delete; + COM(COM &&) = delete; + COM &operator=(const COM &) = delete; + COM &operator=(COM &&) = delete; +}; + +#endif diff --git a/src/win32/ComHeapPtr.hxx b/src/win32/ComHeapPtr.hxx new file mode 100644 index 000000000..dffc085c2 --- /dev/null +++ b/src/win32/ComHeapPtr.hxx @@ -0,0 +1,96 @@ +/* + * Copyright 2020 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_WIN32_COMHEAPPTR_HXX +#define MPD_WIN32_COMHEAPPTR_HXX + +#include +#include +#include +#include + +// RAII for CoTaskMemAlloc and CoTaskMemFree +// https://docs.microsoft.com/zh-tw/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemalloc +// https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemfree +template +class ComHeapPtr { +public: + using pointer = T *; + using element_type = T; + + constexpr ComHeapPtr() noexcept : ptr(nullptr) {} + constexpr ComHeapPtr(std::nullptr_t) noexcept : ptr(nullptr) {} + explicit constexpr ComHeapPtr(pointer p) noexcept : ptr(p) {} + + ComHeapPtr(const ComHeapPtr &u) = delete; + constexpr ComHeapPtr(ComHeapPtr &&u) noexcept : ptr(std::exchange(u.ptr, nullptr)) {} + + ComHeapPtr &operator=(const ComHeapPtr &r) = delete; + constexpr ComHeapPtr &operator=(ComHeapPtr &&r) noexcept { + std::swap(ptr, r.ptr); + return *this; + } + ComHeapPtr &operator=(std::nullptr_t) noexcept { + reset(); + return *this; + } + + ~ComHeapPtr() noexcept { reset(); } + + pointer release() noexcept { return std::exchange(ptr, nullptr); } + void reset() noexcept { + if (ptr) { + CoTaskMemFree(release()); + } + } + void swap(ComHeapPtr &other) noexcept { std::swap(ptr, other.ptr); } + + pointer get() const noexcept { return ptr; } + explicit operator bool() const noexcept { return ptr; } + + auto operator*() const { return *ptr; } + pointer operator->() const noexcept { return ptr; } + + T **Address() noexcept { + reset(); + return &ptr; + } + template + U **AddressCast() noexcept { + reset(); + return reinterpret_cast(&ptr); + } + + template + U *Cast() noexcept { + return reinterpret_cast(ptr); + } + +private: + pointer ptr; +}; + +namespace std { +template +void swap(ComHeapPtr &lhs, ComHeapPtr &rhs) noexcept { + lhs.swap(rhs); +} +} + +#endif diff --git a/src/win32/ComPtr.hxx b/src/win32/ComPtr.hxx new file mode 100644 index 000000000..8b89161f6 --- /dev/null +++ b/src/win32/ComPtr.hxx @@ -0,0 +1,117 @@ +/* + * Copyright 2020 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_WIN32_COMPTR_HXX +#define MPD_WIN32_COMPTR_HXX + +#include "win32/HResult.hxx" +#include +#include +#include +#include + +// RAII for Object in Microsoft Component Object Model(COM) +// https://docs.microsoft.com/zh-tw/windows/win32/api/_com/ +template +class ComPtr { +public: + using pointer = T *; + using element_type = T; + + constexpr ComPtr() noexcept : ptr(nullptr) {} + constexpr ComPtr(std::nullptr_t) noexcept : ptr(nullptr) {} + explicit constexpr ComPtr(pointer p) noexcept : ptr(p) {} + + ComPtr(const ComPtr &u) noexcept : ptr(u.ptr) { + if (ptr) { + ptr->AddRef(); + } + } + constexpr ComPtr(ComPtr &&u) noexcept : ptr(std::exchange(u.ptr, nullptr)) {} + + ComPtr &operator=(const ComPtr &r) noexcept { + reset(); + ptr = r.ptr; + if (ptr) { + ptr->AddRef(); + } + return *this; + } + constexpr ComPtr &operator=(ComPtr &&r) noexcept { + std::swap(ptr, r.ptr); + return *this; + } + ComPtr &operator=(std::nullptr_t) noexcept { + reset(); + return *this; + } + + ~ComPtr() noexcept { reset(); } + + pointer release() noexcept { return std::exchange(ptr, nullptr); } + void reset() noexcept { + if (ptr) { + release()->Release(); + } + } + void swap(ComPtr &other) noexcept { std::swap(ptr, other.ptr); } + + pointer get() const noexcept { return ptr; } + explicit operator bool() const noexcept { return ptr; } + + auto operator*() const { return *ptr; } + pointer operator->() const noexcept { return ptr; } + + void CoCreateInstance(REFCLSID class_id, LPUNKNOWN unknown_outer = nullptr, + DWORD class_context = CLSCTX_ALL) { + HRESULT result = + ::CoCreateInstance(class_id, unknown_outer, class_context, + __uuidof(T), reinterpret_cast(&ptr)); + if (FAILED(result)) { + throw FormatHResultError(result, "Unable to create instance"); + } + } + + T **Address() noexcept { + reset(); + return &ptr; + } + template + U **AddressCast() noexcept { + reset(); + return reinterpret_cast(&ptr); + } + + template + U *Cast() noexcept { + return reinterpret_cast(ptr); + } + +private: + pointer ptr; +}; + +namespace std { +template +void swap(ComPtr &lhs, ComPtr &rhs) noexcept { + lhs.swap(rhs); +} +} + +#endif diff --git a/src/win32/HResult.hxx b/src/win32/HResult.hxx new file mode 100644 index 000000000..4fd92f1cc --- /dev/null +++ b/src/win32/HResult.hxx @@ -0,0 +1,108 @@ +/* + * Copyright 2020 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_WIN32_HRESULT_HXX +#define MPD_WIN32_HRESULT_HXX + +#include "util/Compiler.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +constexpr std::string_view HRESULTToString(HRESULT result) { + using namespace std::literals; + switch (result) { +#define C(x) \ +case x: \ + return #x##sv + C(AUDCLNT_E_ALREADY_INITIALIZED); + C(AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL); + C(AUDCLNT_E_BUFFER_ERROR); + C(AUDCLNT_E_BUFFER_OPERATION_PENDING); + C(AUDCLNT_E_BUFFER_SIZE_ERROR); + C(AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED); + C(AUDCLNT_E_BUFFER_TOO_LARGE); + C(AUDCLNT_E_CPUUSAGE_EXCEEDED); + C(AUDCLNT_E_DEVICE_INVALIDATED); + C(AUDCLNT_E_DEVICE_IN_USE); + C(AUDCLNT_E_ENDPOINT_CREATE_FAILED); + C(AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED); + C(AUDCLNT_E_INVALID_DEVICE_PERIOD); + C(AUDCLNT_E_OUT_OF_ORDER); + C(AUDCLNT_E_SERVICE_NOT_RUNNING); + C(AUDCLNT_E_UNSUPPORTED_FORMAT); + C(AUDCLNT_E_WRONG_ENDPOINT_TYPE); + C(CO_E_NOTINITIALIZED); + C(E_INVALIDARG); + C(E_OUTOFMEMORY); + C(E_POINTER); +#undef C + } + return std::string_view(); +} + +static inline const std::error_category &hresult_category() noexcept; +class HResultCategory : public std::error_category { +public: + const char *name() const noexcept override { return "HRESULT"; } + std::string message(int Errcode) const override { + const auto msg = HRESULTToString(Errcode); + if (!msg.empty()) { + return std::string(msg); + } + char buffer[11]; // "0x12345678\0" + int size = snprintf(buffer, sizeof(buffer), "0x%1x", Errcode); + assert(2 <= size && size <= 10); + return std::string(buffer, size); + } + std::error_condition default_error_condition(int code) const noexcept override { + return std::error_condition(code, hresult_category()); + } +}; +static inline const std::error_category &hresult_category() noexcept { + static const HResultCategory hresult_category_instance{}; + return hresult_category_instance; +} + +gcc_printf(2, 3) static inline std::runtime_error + FormatHResultError(HRESULT result, const char *fmt, ...) noexcept { + std::va_list args1, args2; + va_start(args1, fmt); + va_copy(args2, args1); + + const int size = vsnprintf(nullptr, 0, fmt, args1); + va_end(args1); + assert(size >= 0); + + auto buffer = std::make_unique(size + 1); + vsprintf(buffer.get(), fmt, args2); + va_end(args2); + + return std::system_error(std::error_code(result, hresult_category()), + std::string(buffer.get(), size)); +} + +#endif diff --git a/src/win32/WinEvent.hxx b/src/win32/WinEvent.hxx new file mode 100644 index 000000000..87c856199 --- /dev/null +++ b/src/win32/WinEvent.hxx @@ -0,0 +1,54 @@ +/* + * Copyright 2020 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_WIN32_WINEVENT_HXX +#define MPD_WIN32_WINEVENT_HXX + +#include "system/Error.hxx" +#include + +// RAII for Windows unnamed event object +// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventw + +class WinEvent { +public: + WinEvent() : event(CreateEventW(nullptr, false, false, nullptr)) { + if (!event) { + throw FormatLastError("Error creating events"); + } + } + ~WinEvent() noexcept { CloseHandle(event); } + WinEvent(WinEvent &&) = delete; + WinEvent(const WinEvent &) = delete; + WinEvent &operator=(WinEvent &&) = delete; + WinEvent &operator=(const WinEvent &) = delete; + + HANDLE handle() noexcept { return event; } + + DWORD Wait(DWORD milliseconds) noexcept { + return WaitForSingleObject(event, milliseconds); + } + + bool Set() noexcept { return SetEvent(event); } + +private: + HANDLE event; +}; + +#endif