// SPDX-License-Identifier: LGPL-2.1-or-later #pragma once #include #include #include namespace native_control { inline bool query(HWND window, UINT message, WPARAM wp, LPARAM lp, DWORD_PTR *result) { return SendMessageTimeoutW(window, message, wp, lp, SMTO_ABORTIFHUNG, 100, result) != 0; } // Common-control messages above WM_USER do not marshal embedded pointers. // Own the target allocation and process handle together, including failures. class RemoteBuffer { public: RemoteBuffer(HWND window, size_t size) { DWORD processId = 0; GetWindowThreadProcessId(window, &processId); process_ = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId); if (!process_ || !IsWow64Process(process_, &wow64_)) return; memory_ = VirtualAllocEx(process_, nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // Every byte of the allocation must be addressable by the target. if (memory_ && wow64_ && (address() > UINT32_MAX || size - 1 > UINT32_MAX - address())) { VirtualFreeEx(process_, memory_, 0, MEM_RELEASE); memory_ = nullptr; } } ~RemoteBuffer() { if (memory_) VirtualFreeEx(process_, memory_, 0, MEM_RELEASE); if (process_) CloseHandle(process_); } RemoteBuffer(const RemoteBuffer &) = delete; RemoteBuffer &operator=(const RemoteBuffer &) = delete; explicit operator bool() const { return memory_ != nullptr; } bool is32Bit() const { return wow64_ != FALSE; } uintptr_t address(size_t offset = 0) const { return reinterpret_cast(memory_) + offset; } bool write(const void *buffer, size_t size) const { return WriteProcessMemory(process_, memory_, buffer, size, nullptr) != 0; } bool read(void *buffer, size_t size, size_t offset = 0) const { return ReadProcessMemory(process_, reinterpret_cast(address(offset)), buffer, size, nullptr) != 0; } private: HANDLE process_ = nullptr; void *memory_ = nullptr; BOOL wow64_ = FALSE; }; } // namespace native_control