#ifndef ZCPPWIN_WINTOOL_H #define ZCPPWIN_WINTOOL_H // ============================================================================ // wintool.h // ---------------------------------------------------------------------------- // MSVC / Windows / C++20 常用 Win32 工具库声明文件。 // // 使用方式: // 1. 将 wintool.h 与 wintool.cpp 加入你的 CLion + CMake 工程。 // 2. 推荐在 CMake 中启用 C++20,并定义 UNICODE、_UNICODE、NOMINMAX。 // 3. 所有接口都位于命名空间 Z 中,直接调用 Z::xxx 即可。 // // 设计约定: // - 默认优先使用宽字符版本,也就是 std::wstring / Win32 W 系列 API。 // - 失败时大多数函数会抛出 Z::Win32Error、Z::HResultError 或 std::runtime_error。 // - 资源对象采用 RAII,离开作用域后自动释放 HANDLE、HMODULE、HKEY 等资源。 // - 返回 std::optional 的函数表示“失败或不存在”不一定是异常场景。 // // 简单示例: // ZWINTOOL::SetConsoleUtf8(); // auto exe = ZWINTOOL::ModulePathW(); // ZWINTOOL::WriteTextFileUtf8(L"log.txt", "hello\n"); // auto text = ZWINTOOL::ReadTextFileUtf8(L"log.txt"); // ============================================================================ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #ifndef NOMINMAX #define NOMINMAX #endif #include #include #include #include #include #include #include #include #include #include #include "ztool_lib_macro.h" namespace ZWINTOOL { // 通用字节数组类型,常用于二进制文件、注册表 REG_BINARY 等场景。 using Bytes = std::vector; /* ---------------------------------------------------------------------------- */ //region 错误处理工具 /* ---------------------------------------------------------------------------- */ // Win32 错误异常。 // 作用:保存 GetLastError / Win32 API 返回的错误码,并生成可读错误文本。 // 用法:catch (const Z::Win32Error& e) 后可通过 e.code() 获取 DWORD 错误码。 class ZTOOL_API Win32Error final : public std::runtime_error { public: // error 为 Win32 错误码;context 用于标记失败位置,例如 "CreateFileW"。 explicit Win32Error(DWORD error, std::string_view context = {}); // 返回原始 Win32 错误码。 [[nodiscard]] DWORD code() const noexcept; private: DWORD code_{}; }; // HRESULT 错误异常。 // 作用:包装 COM、Shell、DirectX 等 API 常见的 HRESULT 失败值。 // 用法:catch (const Z::HResultError& e) 后可通过 e.hr() 获取 HRESULT。 class ZTOOL_API HResultError final : public std::runtime_error { public: // hr 为 HRESULT;context 用于标记失败位置,例如 "CoCreateInstance"。 explicit HResultError(HRESULT hr, std::string_view context = {}); // 返回原始 HRESULT。 [[nodiscard]] HRESULT hr() const noexcept; private: HRESULT hr_{}; }; // 获取当前线程最近一次 Win32 错误码,相当于 GetLastError()。 [[nodiscard]] ZTOOL_API DWORD LastError() noexcept; // 将 Win32 错误码格式化为宽字符串。 // 用法:std::wstring msg = Z::ErrorMessageW(ERROR_FILE_NOT_FOUND); [[nodiscard]] ZTOOL_API std::wstring ErrorMessageW(DWORD error = ::GetLastError()); // 将 Win32 错误码格式化为 UTF-8 字符串。 [[nodiscard]] ZTOOL_API std::string ErrorMessageA(DWORD error = ::GetLastError()); // 将 HRESULT 格式化为宽字符串。 [[nodiscard]] ZTOOL_API std::wstring HResultMessageW(HRESULT hr); // 将 HRESULT 格式化为 UTF-8 字符串。 [[nodiscard]] ZTOOL_API std::string HResultMessageA(HRESULT hr); // 直接抛出当前 GetLastError() 对应的 Win32Error。 // 用法:if (!ok) Z::ThrowLastError("SomeWin32Api"); ZTOOL_API void ThrowLastError(std::string_view context = {}); // 检查 BOOL 返回值;为 FALSE 时抛出 Win32Error。 ZTOOL_API void ThrowIfFalse(BOOL ok, std::string_view context = {}); // 检查指针;为空时抛出 Win32Error。 ZTOOL_API void ThrowIfNull(const void *pointer, std::string_view context = {}); // 检查 HANDLE;nullptr 或 INVALID_HANDLE_VALUE 时抛出 Win32Error。 ZTOOL_API void ThrowIfInvalidHandle(HANDLE handle, std::string_view context = {}); // 检查 HRESULT;FAILED(hr) 时抛出 HResultError。 ZTOOL_API void CheckHResult(HRESULT hr, std::string_view context = {}); //endregion /* ---------------------------------------------------------------------------- */ //region 字符串编码转换工具 /* ---------------------------------------------------------------------------- */ // UTF-8 转 UTF-16。 // 典型用途:把配置文件、网络文本、std::string 传给 Windows W 系列 API。 [[nodiscard]] ZTOOL_API std::wstring Utf8ToWide(std::string_view text); // UTF-16 转 UTF-8。 // 典型用途:把 Win32 返回的 std::wstring 输出到日志、文件或控制台。 [[nodiscard]] ZTOOL_API std::string WideToUtf8(std::wstring_view text); // 指定代码页的多字节字符串转 UTF-16。 // 用法:Z::MultiByteToWide(text, CP_ACP) 或 Z::MultiByteToWide(text, CP_UTF8)。 [[nodiscard]] ZTOOL_API std::wstring MultiByteToWide(std::string_view text, UINT codePage); // UTF-16 转指定代码页多字节字符串。 [[nodiscard]] ZTOOL_API std::string WideToMultiByte(std::wstring_view text, UINT codePage); //endregion /* ---------------------------------------------------------------------------- */ //region RAII Win32 资源封装 /* ---------------------------------------------------------------------------- */ // HANDLE RAII 封装。 // 作用:自动调用 CloseHandle,避免文件、进程、线程、事件等句柄泄漏。 // 用法:Z::Handle file(::CreateFileW(...)); if (file) { ... } class ZTOOL_API Handle final { public: constexpr Handle() noexcept = default; // 接管一个已有 HANDLE;析构时自动 CloseHandle。 explicit Handle(HANDLE handle) noexcept; ~Handle() noexcept; Handle(const Handle &) = delete; Handle &operator=(const Handle &) = delete; Handle(Handle &&other) noexcept; Handle &operator=(Handle &&other) noexcept; // 获取原始 HANDLE,不转移所有权。 [[nodiscard]] HANDLE get() const noexcept; // 获取用于输出参数的地址;会先释放旧句柄。 // 用法:SomeApi(handle.put()); [[nodiscard]] HANDLE *put() noexcept; // 判断是否持有有效句柄。 [[nodiscard]] bool valid() const noexcept; explicit operator bool() const noexcept; // 放弃所有权并返回原始 HANDLE;调用者之后需要自行 CloseHandle。 [[nodiscard]] HANDLE release() noexcept; // 释放旧句柄并接管新句柄。 void reset(HANDLE handle = nullptr) noexcept; private: HANDLE handle_ = nullptr; }; // HMODULE RAII 封装。 // 作用:LoadLibraryW / FreeLibrary 的安全封装,适合动态加载 DLL。 // 用法:Z::Library dll(L"user32.dll"); auto p = dll.proc("MessageBoxW"); class ZTOOL_API Library final { public: Library() noexcept = default; explicit Library(std::wstring_view path); ~Library() noexcept; Library(const Library &) = delete; Library &operator=(const Library &) = delete; Library(Library &&other) noexcept; Library &operator=(Library &&other) noexcept; [[nodiscard]] HMODULE get() const noexcept; [[nodiscard]] bool valid() const noexcept; explicit operator bool() const noexcept; // 加载指定 DLL;已加载其他模块时会先释放。 void load(std::wstring_view path); // 放弃所有权,调用者之后需要自行 FreeLibrary。 [[nodiscard]] HMODULE release() noexcept; // 释放旧模块并接管新模块。 void reset(HMODULE module = nullptr) noexcept; // 获取 DLL 导出函数地址,并转换为指定函数指针类型。 // 用法:using Fn = int (WINAPI*)(HWND,LPCWSTR,LPCWSTR,UINT); // auto MessageBoxWFn = dll.proc("MessageBoxW"); template [[nodiscard]] T proc(std::string_view name) const { if (!module_) { throw std::logic_error("Z::Library::proc called on empty library"); } std::string procName{name}; FARPROC p = ::GetProcAddress(module_, procName.c_str()); if (!p) { ThrowLastError("GetProcAddress"); } return reinterpret_cast(p); } private: HMODULE module_ = nullptr; }; // COM 初始化 RAII 封装。 // 作用:构造时 CoInitializeEx,析构时 CoUninitialize。 // 用法:Z::ComApartment com; 然后安全调用 COM / Shell API。 class ZTOOL_API ComApartment final { public: // coinit 可传 COINIT_APARTMENTTHREADED 或 COINIT_MULTITHREADED。 explicit ComApartment(DWORD coinit = COINIT_APARTMENTTHREADED); ~ComApartment() noexcept; ComApartment(const ComApartment &) = delete; ComApartment &operator=(const ComApartment &) = delete; // 返回 CoInitializeEx 的结果,通常 S_OK 或 S_FALSE 表示可用。 [[nodiscard]] HRESULT hr() const noexcept; private: HRESULT hr_ = S_OK; bool initialized_ = false; }; //endregion /* ---------------------------------------------------------------------------- */ //region 路径与环境变量工具 /* ---------------------------------------------------------------------------- */ // 获取当前 EXE 或指定模块的完整路径。 [[nodiscard]] ZTOOL_API std::wstring ModulePathW(HMODULE module = nullptr); // 获取当前 EXE 或指定模块所在目录。 [[nodiscard]] ZTOOL_API std::wstring ModuleDirW(HMODULE module = nullptr); // 获取当前工作目录。 [[nodiscard]] ZTOOL_API std::wstring CurrentDirectoryW(); // 获取系统临时目录。 [[nodiscard]] ZTOOL_API std::wstring TempDirectoryW(); // 获取父目录路径。示例:C:\\a\\b.txt -> C:\\a。 [[nodiscard]] ZTOOL_API std::wstring ParentPathW(std::wstring_view path); // 获取文件名部分。示例:C:\\a\\b.txt -> b.txt。 [[nodiscard]] ZTOOL_API std::wstring FileNameW(std::wstring_view path); // 拼接两个路径片段,会自动处理反斜杠。 [[nodiscard]] ZTOOL_API std::wstring JoinPathW(std::wstring_view left, std::wstring_view right); // 转换为 Windows 长路径形式。绝对路径会加 \\?\ 前缀,便于突破 MAX_PATH 限制。 [[nodiscard]] ZTOOL_API std::wstring ToLongPathW(std::wstring_view path); // 读取环境变量;不存在时返回 std::nullopt。 [[nodiscard]] ZTOOL_API std::optional EnvironmentVariableW(std::wstring_view name); // 设置环境变量;value 为 nullopt 时删除环境变量。 ZTOOL_API void SetEnvironmentVariableWZ(std::wstring_view name, std::optional value); // 展开字符串中的环境变量。示例:%TEMP%\\a.txt。 [[nodiscard]] ZTOOL_API std::wstring ExpandEnvironmentStringsWZ(std::wstring_view text); //endregion /* ---------------------------------------------------------------------------- */ //region 文件系统工具 /* ---------------------------------------------------------------------------- */ // 判断路径是否存在,文件或目录均可。 [[nodiscard]] ZTOOL_API bool PathExistsW(std::wstring_view path); // 判断路径是否存在且为普通文件。 [[nodiscard]] ZTOOL_API bool FileExistsW(std::wstring_view path); // 判断路径是否存在且为目录。 [[nodiscard]] ZTOOL_API bool DirectoryExistsW(std::wstring_view path); // 递归创建目录。目录已存在时返回 true。 [[nodiscard]] ZTOOL_API bool CreateDirectoryTreeW(std::wstring_view path); // 读取整个文件为字节数组。失败时抛出 Win32Error。 [[nodiscard]] ZTOOL_API Bytes ReadFileBytes(std::wstring_view path); // 写入字节数组到文件;append=true 时追加写入,否则覆盖。 ZTOOL_API void WriteFileBytes(std::wstring_view path, std::span data, bool append = false); // 按 UTF-8 文本读取文件。不会自动去除 BOM。 [[nodiscard]] ZTOOL_API std::string ReadTextFileUtf8(std::wstring_view path); // 按 UTF-8 文本写入文件;writeBom=true 时写入 UTF-8 BOM。 ZTOOL_API void WriteTextFileUtf8(std::wstring_view path, std::string_view text, bool append = false, bool writeBom = false); //endregion /* ---------------------------------------------------------------------------- */ //region 进程与命令行工具 /* ---------------------------------------------------------------------------- */ // 启动进程参数。 // application:可执行文件路径,可为空。 // commandLine:完整命令行;如果 application 为空,应包含可执行文件。 // workingDirectory:工作目录,可为空。 // creationFlags:CreateProcessW 标志。 // inheritHandles:是否继承句柄。 // showWindow/showCommand:控制窗口显示状态。 struct ZTOOL_API ProcessStartInfo { std::wstring application; std::wstring commandLine; std::wstring workingDirectory; DWORD creationFlags = 0; bool inheritHandles = false; bool showWindow = true; WORD showCommand = SW_SHOWNORMAL; }; // 启动进程后的结果。 // process/thread 使用 RAII 管理,离开作用域自动 CloseHandle。 struct ZTOOL_API ProcessInfo { Handle process; Handle thread; DWORD processId = 0; DWORD threadId = 0; }; // 获取当前进程命令行参数,等价于 main/wmain 的 argv。 [[nodiscard]] ZTOOL_API std::vector CommandLineArgsW(); // 将单个参数转义为 Windows 命令行安全格式。 [[nodiscard]] ZTOOL_API std::wstring QuoteCommandLineArgW(std::wstring_view arg); // 把多个参数拼接成安全命令行。 [[nodiscard]] ZTOOL_API std::wstring BuildCommandLineW(std::span args); // 启动进程。失败时抛出 Win32Error。 [[nodiscard]] ZTOOL_API ProcessInfo StartProcess(const ProcessStartInfo &info); // 等待进程结束。 // 返回:进程退出码;如果超时返回 std::nullopt。 [[nodiscard]] ZTOOL_API std::optional WaitForProcess(HANDLE process, DWORD milliseconds = INFINITE); // 判断当前进程是否以管理员权限运行。 [[nodiscard]] ZTOOL_API bool IsProcessElevated(); // 获取当前 Windows 用户名。 [[nodiscard]] ZTOOL_API std::wstring UserNameW(); // 获取计算机名。 [[nodiscard]] ZTOOL_API std::wstring ComputerNameW(); // 获取当前进程 ID。 [[nodiscard]] ZTOOL_API DWORD CurrentProcessId() noexcept; // 获取当前线程 ID。 [[nodiscard]] ZTOOL_API DWORD CurrentThreadId() noexcept; //endregion /* ---------------------------------------------------------------------------- */ //region 注册表工具 /* ---------------------------------------------------------------------------- */ // HKEY RAII 封装。 // 作用:自动 RegCloseKey,提供常用注册表读写和枚举功能。 // 用法: // auto key = Z::RegKey::Open(HKEY_CURRENT_USER, L"Software\\MyApp"); // auto name = key.GetStringValue(L"Name"); class ZTOOL_API RegKey final { public: RegKey() noexcept = default; // 接管已有 HKEY;析构时自动 RegCloseKey。 explicit RegKey(HKEY key) noexcept; ~RegKey() noexcept; RegKey(const RegKey &) = delete; RegKey &operator=(const RegKey &) = delete; RegKey(RegKey &&other) noexcept; RegKey &operator=(RegKey &&other) noexcept; // 打开注册表键;失败时抛出 Win32Error。 static RegKey Open(HKEY root, std::wstring_view subKey, REGSAM access = KEY_READ); // 尝试打开注册表键;不存在时返回 std::nullopt,其他错误抛异常。 static std::optional TryOpen(HKEY root, std::wstring_view subKey, REGSAM access = KEY_READ); // 创建或打开注册表键。 static RegKey Create(HKEY root, std::wstring_view subKey, REGSAM access = KEY_READ | KEY_WRITE); [[nodiscard]] HKEY get() const noexcept; [[nodiscard]] HKEY *put() noexcept; [[nodiscard]] bool valid() const noexcept; explicit operator bool() const noexcept; [[nodiscard]] HKEY release() noexcept; void reset(HKEY key = nullptr) noexcept; // 读取字符串值。支持 REG_SZ / REG_EXPAND_SZ;不存在返回 nullopt。 [[nodiscard]] std::optional GetStringValue( std::wstring_view valueName = {}) const; // 写入字符串值。type 通常为 REG_SZ 或 REG_EXPAND_SZ。 void SetStringValue(std::wstring_view valueName, std::wstring_view value, DWORD type = REG_SZ) const; // 读取 DWORD 值;不存在返回 nullopt。 [[nodiscard]] std::optional GetDwordValue( std::wstring_view valueName = {}) const; // 写入 DWORD 值。 void SetDwordValue(std::wstring_view valueName, DWORD value) const; // 读取 QWORD 值;不存在返回 nullopt。 [[nodiscard]] std::optional GetQwordValue( std::wstring_view valueName = {}) const; // 写入 QWORD 值。 void SetQwordValue(std::wstring_view valueName, unsigned long long value) const; // 读取二进制值;不存在返回 nullopt。 [[nodiscard]] std::optional GetBinaryValue( std::wstring_view valueName = {}) const; // 写入二进制值。 void SetBinaryValue(std::wstring_view valueName, std::span data) const; // 枚举子键名。 [[nodiscard]] std::vector EnumSubKeys() const; // 枚举值名。 [[nodiscard]] std::vector EnumValueNames() const; private: HKEY key_ = nullptr; }; //endregion /* ---------------------------------------------------------------------------- */ //region 窗口工具 /* ---------------------------------------------------------------------------- */ // 获取窗口标题文本。 [[nodiscard]] ZTOOL_API std::wstring WindowTextW(HWND hwnd); // 获取窗口标题文本并转换为 UTF-8。 [[nodiscard]] ZTOOL_API std::string WindowTextUtf8(HWND hwnd); // 使用 UTF-8 文本设置窗口标题。 ZTOOL_API void SetWindowTextUtf8(HWND hwnd, std::string_view text); // 获取窗口类名。 [[nodiscard]] ZTOOL_API std::wstring ClassNameW(HWND hwnd); // 获取窗口屏幕坐标矩形。 [[nodiscard]] ZTOOL_API RECT WindowRect(HWND hwnd); // 获取窗口客户区矩形。 [[nodiscard]] ZTOOL_API RECT ClientRect(HWND hwnd); // 将窗口移动到父窗口或屏幕中央。 ZTOOL_API void CenterWindow(HWND hwnd, HWND parent = nullptr); //endregion /* ---------------------------------------------------------------------------- */ //region 剪贴板与控制台工具 /* ---------------------------------------------------------------------------- */ // 设置剪贴板 Unicode 文本。 ZTOOL_API void SetClipboardTextW(std::wstring_view text, HWND owner = nullptr); // 读取剪贴板 Unicode 文本;没有文本时返回 nullopt。 [[nodiscard]] ZTOOL_API std::optional GetClipboardTextW(HWND owner = nullptr); // 将控制台输入/输出代码页设置为 UTF-8。 // 常用于 main 开头,配合中文日志输出。 ZTOOL_API void SetConsoleUtf8(); // 判断当前进程是否已经拥有控制台。 [[nodiscard]] ZTOOL_API bool HasConsole() noexcept; // 尝试附加父进程控制台;失败则分配一个新控制台。 [[nodiscard]] ZTOOL_API bool AttachOrAllocConsole(); //endregion } // namespace Z #endif //ZCPPWIN_WINTOOL_H