#pragma once #include #include "shared/strings.hpp" #include #include #include #include #include #define COM_CALLCONV __stdcall namespace com { inline HRESULT checked(HRESULT result, const std::source_location loc = std::source_location::current()) { if (result == 0) { return result; } auto msg = std::format("Got HRESULT={:#x} at\n{}:{}", static_cast(result) & 0xFFFFFFFF, loc.function_name(), loc.line()); throw std::runtime_error(msg); } template inline HRESULT retry_while_pending(Callable&& fn) { bool delayed = false; HRESULT status = 0; do { if (status != 0) { delayed = true; std::println("delaying for com retry..."); std::this_thread::sleep_for(std::chrono::milliseconds(500)); } status = fn(); } while (status == E_PENDING); return status; } /// A very basic implementation, a lot of stuff is missing template class Ptr { public: Ptr() = default; explicit Ptr(Ty* ptr): ptr_(ptr) { } ~Ptr() { release(); } /// No copying Ptr(const Ptr&) = delete; Ptr& operator=(const Ptr&) = delete; /// Move Ptr(Ptr&& other) noexcept: ptr_(other.ptr_) { other.ptr_ = nullptr; } Ptr& operator=(Ptr&& other) noexcept { if (this != &other) { release(); ptr_ = other.ptr_; other.ptr_ = nullptr; } return *this; } public: [[nodiscard]] Ty* get() const { return ptr_; } [[nodiscard]] Ty* operator->() const { return ptr_; } [[nodiscard]] Ty** ref_to_ptr() { return &ptr_; } private: void release() { if (ptr_ != nullptr) { ptr_->Release(); } } Ty* ptr_ = nullptr; }; template class IBaseObject { public: static constexpr GUID kClsId = ClsId; static constexpr GUID kIID = IID; private: virtual HRESULT COM_CALLCONV QueryInterface() = 0; virtual std::uint32_t COM_CALLCONV AddRef() = 0; public: virtual std::uint32_t COM_CALLCONV Release() = 0; }; template concept ComObject = requires { Ty::kClsId; Ty::kIID; }; template [[nodiscard]] Ptr query() { Ptr result; const auto status = CoCreateInstance(Ty::kClsId, 0, 1, Ty::kIID, reinterpret_cast(result.ref_to_ptr())); if (status == REGDB_E_CLASSNOTREG) { throw std::runtime_error(strings::wsc_unavailable_error().data()); } checked(status); return result; } } // namespace com