[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f3xghkVjnccAbQm1KyvkiVFGRvGcdI7l4xOy3-m9OJDg":3,"$fJU-4tot_gC5fDkujNeoE-cGsdMy5V_KcdUXLuAnTFgw":16,"$fkttOZCf2qXdM2kY37UQx7ZObd1ENIC6tGz9V3qY1sPc":423},{"slug":4,"title":5,"description":6,"content":7,"content_html":8,"pub_date":9,"tags":10,"draft":15},"win-cw2a-ca2w","ATL 字符串转换：CW2A 与 CA2W 完全指南","详解 ATL 宏 CW2A\u002FCA2W 在 Unicode 与 ANSI 之间的字符串转换用法、头文件依赖、USES_CONVERSION 宏的作用与常见陷阱。","# ATL 字符串转换：CW2A 与 CA2W 完全指南\n\n在 Windows C++ 开发中，字符编码转换是绕不开的话题。ATL（Active Template Library）提供了一套简洁易用的字符串转换宏，其中 `CW2A` 和 `CA2W` 最为常用。本文从 Windows 字符编码历史讲起，深入解析这套转换机制。\n\n## Windows 字符编码历史\n\n### ANSI 时代（Win9x）\n\n早期 Windows 使用 ANSI 字符集（实际是各地区的代码页，如 GBK、Shift-JIS 等）。API 函数有 `A` 后缀版本，如 `CreateWindowA`，接受 `char*` 参数。\n\n### Unicode 时代（WinNT\u002F2000+）\n\nWindows NT 内核原生使用 UTF-16LE（Windows 称之为\"Unicode\"）。API 函数有 `W` 后缀版本，如 `CreateWindowW`，接受 `wchar_t*` 参数。\n\n现代 Windows 推荐使用 Unicode（W 系列）API。当你包含 `\u003Cwindows.h>` 并定义 `UNICODE` 宏时，`CreateWindow` 会自动映射到 `CreateWindowW`。\n\n### 字符类型对照\n\n| 类型 | 宽度 | 编码 | 典型用途 |\n|------|------|------|----------|\n| `char` | 1 字节 | ANSI\u002FUTF-8\u002FGBK | 窄字符，`A` 系列 API |\n| `wchar_t` | 2 字节（Windows） | UTF-16LE | 宽字符，`W` 系列 API |\n| `TCHAR` | 依编译选项 | ANSI 或 Unicode | 兼容两者（已过时） |\n| `CHAR` | 1 字节 | ANSI | Win32 类型别名 |\n| `WCHAR` | 2 字节 | UTF-16LE | Win32 类型别名 |\n\n## ATL 转换宏原理\n\nATL 转换宏（定义在 `\u003Catlconv.h>` 或 `\u003Catlbase.h>`）利用 C++ 栈上的临时对象实现字符串转换。其核心原理：\n\n1. 构造时分配转换缓冲区（小字符串用栈，大字符串用堆）\n2. 调用 `MultiByteToWideChar` 或 `WideCharToMultiByte` 进行实际转换\n3. 析构时自动释放堆内存（如果用了堆）\n4. 提供隐式转换运算符，可直接当作指针使用\n\n宏名称的命名规则：`C{源编码}2{目标编码}[EX]`\n\n- **W** = Wide（宽字符，`wchar_t*`，UTF-16LE）\n- **A** = ANSI（窄字符，`char*`，当前 ANSI 代码页）\n- **T** = TCHAR（依 `UNICODE` 宏决定）\n- **U** = UTF-8（`char*`，UTF-8 编码）\n- **OLE** = OLESTR（`OLECHAR*`，通常等同于 `WCHAR*`）\n- **EX** 后缀 = 扩展版本，支持显式指定代码页\n\n## 主要宏详解\n\n### CW2A — Wide 转 ANSI\n\n```cpp\n#include \u003Catlconv.h>\n\nvoid Example_CW2A() {\n    const wchar_t* wstr = L\"Hello, 世界\";\n    \n    \u002F\u002F 基本用法：转为系统默认 ANSI 代码页（如 GBK）\n    CW2A narrow(wstr);\n    printf(\"ANSI: %s\n\", (LPCSTR)narrow);  \u002F\u002F 或直接 printf(\"%s\", narrow);\n    \n    \u002F\u002F 指定 UTF-8 代码页\n    CW2A utf8(wstr, CP_UTF8);\n    printf(\"UTF-8: %s\n\", (LPCSTR)utf8);\n    \n    \u002F\u002F 用于需要 LPCSTR 的函数\n    SomeFunctionNeedingAnsi((LPCSTR)CW2A(wstr));\n}\n```\n\n### CA2W — ANSI 转 Wide\n\n```cpp\nvoid Example_CA2W() {\n    const char* astr = \"Hello World\";\n    \n    \u002F\u002F 基本用法：从系统默认代码页转 UTF-16\n    CA2W wide(astr);\n    wprintf(L\"Wide: %s\n\", (LPCWSTR)wide);\n    \n    \u002F\u002F 从 UTF-8 转 UTF-16（现代推荐方式）\n    const char* utf8str = u8\"Hello, 世界\";\n    CA2W wideFromUtf8(utf8str, CP_UTF8);\n    wprintf(L\"From UTF-8: %s\n\", (LPCWSTR)wideFromUtf8);\n    \n    \u002F\u002F 赋值给 CString\n    CStringW cstr = CA2W(astr);\n}\n```\n\n### CT2A 和 CT2W — TCHAR 转换\n\n```cpp\n\u002F\u002F CT2A: TCHAR -> ANSI\n\u002F\u002F CT2W: TCHAR -> Wide\n\u002F\u002F CA2T: ANSI -> TCHAR\n\u002F\u002F CW2T: Wide -> TCHAR\n\nvoid Example_TCHAR() {\n    LPCTSTR tstr = _T(\"Hello\");\n    \n    CT2A ansi(tstr);           \u002F\u002F 转为 ANSI\n    CT2W wide(tstr);           \u002F\u002F 转为 Wide（若已是 Wide 则无操作）\n    \n    CA2T fromAnsi(\"Hello\");    \u002F\u002F ANSI 转 TCHAR\n}\n```\n\n### CW2CA \u002F CA2CW — Const 版本\n\n加 `C` 表示返回 `const` 指针版本，语义更安全：\n\n```cpp\nCW2CA  \u002F\u002F const LPSTR\nCA2CW  \u002F\u002F const LPWSTR\n```\n\n通常优先使用带 `C` 的版本，除非你确实需要修改转换结果。\n\n### 完整宏列表\n\n| 宏 | 源类型 | 目标类型 |\n|----|--------|----------|\n| `CA2W` \u002F `CA2CW` | `char*` (ANSI) | `wchar_t*` |\n| `CW2A` \u002F `CW2CA` | `wchar_t*` | `char*` (ANSI) |\n| `CA2T` \u002F `CA2CT` | `char*` (ANSI) | `TCHAR*` |\n| `CT2A` \u002F `CT2CA` | `TCHAR*` | `char*` (ANSI) |\n| `CW2T` \u002F `CW2CT` | `wchar_t*` | `TCHAR*` |\n| `CT2W` \u002F `CT2CW` | `TCHAR*` | `wchar_t*` |\n| `CA2OLE` | `char*` (ANSI) | `OLECHAR*` |\n| `COLE2A` | `OLECHAR*` | `char*` (ANSI) |\n\n## 代码页参数\n\n`EX` 版本或第二参数允许指定代码页：\n\n```cpp\n\u002F\u002F 常用代码页\nCA2W(str, CP_ACP);    \u002F\u002F 系统默认 ANSI（通常 GBK in China）\nCA2W(str, CP_UTF8);   \u002F\u002F UTF-8（强烈推荐）\nCA2W(str, CP_OEMCP);  \u002F\u002F OEM 代码页（控制台）\nCA2W(str, 936);       \u002F\u002F 明确指定 GBK\nCA2W(str, 65001);     \u002F\u002F UTF-8（数字形式）\nCA2W(str, 1252);      \u002F\u002F Windows-1252（西欧）\n```\n\n在现代 Windows 开发中，强烈建议：\n- 所有 `char*` 字符串使用 UTF-8 编码\n- 转换时明确指定 `CP_UTF8`\n- 避免依赖系统默认代码页（`CP_ACP`），因为不同机器可能不同\n\n## 与 WideCharToMultiByte 对比\n\nATL 宏本质上是对 `MultiByteToWideChar` \u002F `WideCharToMultiByte` 的封装：\n\n```cpp\n\u002F\u002F 手动方式（WideCharToMultiByte）\nstd::string WideToUtf8(const std::wstring& wide) {\n    if (wide.empty()) return \"\";\n    \n    int size = WideCharToMultiByte(\n        CP_UTF8, 0,\n        wide.c_str(), (int)wide.size(),\n        nullptr, 0,\n        nullptr, nullptr\n    );\n    \n    std::string result(size, 0);\n    WideCharToMultiByte(\n        CP_UTF8, 0,\n        wide.c_str(), (int)wide.size(),\n        &result[0], size,\n        nullptr, nullptr\n    );\n    return result;\n}\n\n\u002F\u002F ATL 方式（等价，但简洁得多）\nstd::string WideToUtf8_ATL(const std::wstring& wide) {\n    CW2A utf8(wide.c_str(), CP_UTF8);\n    return std::string((LPCSTR)utf8);\n}\n```\n\n| 对比项 | ATL 宏 | WideCharToMultiByte |\n|--------|--------|---------------------|\n| 代码量 | 极少 | 较多（需两次调用） |\n| 安全性 | 高（RAII） | 需手动管理缓冲 |\n| 灵活性 | 中 | 高（更多控制标志） |\n| 依赖 | 需要 ATL | 仅 Win32 |\n| 错误处理 | 有限 | 可通过 GetLastError |\n| 性能 | 略有封装开销 | 略高 |\n\n## 常见坑点\n\n### 坑 1：临时对象生命周期\n\n```cpp\n\u002F\u002F ❌ 危险！CW2A 是临时对象，函数返回后 LPCSTR 悬空\nLPCSTR GetNarrow(const wchar_t* wide) {\n    return CW2A(wide);  \u002F\u002F 返回指向已销毁临时对象的指针！\n}\n\n\u002F\u002F ✅ 正确：保存对象，再返回指针\nvoid UseNarrow(const wchar_t* wide) {\n    CW2A narrow(wide);\n    UseString((LPCSTR)narrow);  \u002F\u002F 在 narrow 的生命周期内使用\n}\n```\n\n### 坑 2：nullptr 输入\n\n```cpp\n\u002F\u002F ❌ 可能崩溃\nconst wchar_t* wstr = nullptr;\nCW2A narrow(wstr);  \u002F\u002F ATL 宏通常不处理 nullptr\n\n\u002F\u002F ✅ 先检查\nif (wstr) {\n    CW2A narrow(wstr);\n    \u002F\u002F ...\n}\n```\n\n### 坑 3：大字符串的隐式堆分配\n\nATL 转换宏默认使用固定大小的栈缓冲区（通常 128 字节），超出后切换到堆。这是自动的，但要注意：如果在循环中频繁转换大字符串，性能可能不如直接使用 `WideCharToMultiByte`。\n\n```cpp\n\u002F\u002F 对于大量数据，直接用 Win32 API 或标准库更好\nfor (auto& item : largeList) {\n    \u002F\u002F ⚠️ 可能频繁堆分配\n    CW2A narrow(item.c_str());\n    \u002F\u002F ...\n}\n```\n\n### 坑 4：不要修改转换后的内容\n\n```cpp\nCW2CA narrow(wstr);  \u002F\u002F const 版本\n\u002F\u002F LPSTR p = narrow;  \u002F\u002F 编译错误（好事！）\nLPCSTR p = narrow;   \u002F\u002F OK\n\nCW2A narrow2(wstr);  \u002F\u002F 非 const 版本\nLPSTR p2 = narrow2;  \u002F\u002F 可以修改，但通常不推荐\n```\n\n### 坑 5：代码页不一致\n\n```cpp\n\u002F\u002F 文件以 GBK 保存，但用 UTF-8 解析 → 乱码\nCA2W w1(gbkStr, CP_UTF8);  \u002F\u002F ❌ 错误的代码页\n\n\u002F\u002F 明确指定正确的代码页\nCA2W w2(gbkStr, 936);       \u002F\u002F ✅ GBK\nCA2W w3(utf8Str, CP_UTF8);  \u002F\u002F ✅ UTF-8\n```\n\n## 最佳实践\n\n### 1. 优先使用 Unicode（W 系列）API\n\n```cpp\n\u002F\u002F 现代 Windows 开发推荐\n#define UNICODE\n#define _UNICODE\n#include \u003Cwindows.h>\n\n\u002F\u002F 这样 CreateWindow 就是 CreateWindowW\nCreateWindow(L\"MyClass\", L\"Title\", ...);\n```\n\n### 2. 内部统一使用 std::wstring 或 UTF-8 std::string\n\n```cpp\n\u002F\u002F 方案A：全程 wstring（Windows 优先）\nstd::wstring title = L\"应用标题\";\nSetWindowTextW(hwnd, title.c_str());\n\n\u002F\u002F 方案B：全程 UTF-8 string（跨平台友好）\nstd::string title_utf8 = u8\"应用标题\";\nCA2W title_wide(title_utf8.c_str(), CP_UTF8);\nSetWindowTextW(hwnd, title_wide);\n```\n\n### 3. 边界处理函数封装\n\n```cpp\n\u002F\u002F 推荐的转换工具函数\nstd::wstring Utf8ToWide(const std::string& utf8) {\n    if (utf8.empty()) return L\"\";\n    CA2W wide(utf8.c_str(), CP_UTF8);\n    return std::wstring((LPCWSTR)wide);\n}\n\nstd::string WideToUtf8(const std::wstring& wide) {\n    if (wide.empty()) return \"\";\n    CW2A utf8(wide.c_str(), CP_UTF8);\n    return std::string((LPCSTR)utf8);\n}\n```\n\n### 4. C++17 以上考虑使用标准库\n\nC++17 引入了 `\u003Cfilesystem>`，C++20 引入了 `\u003Cformat>`。对于跨平台项目，可以使用第三方库如 `utfcpp` 或 `icu`，减少对 ATL 的依赖。\n\nATL 的字符串转换宏虽然简单，但理解其原理和陷阱，才能在 Windows 开发中正确无误地处理字符编码。\n","\u003Ch1>ATL 字符串转换：CW2A 与 CA2W 完全指南\u003C\u002Fh1>\n\u003Cp>在 Windows C++ 开发中，字符编码转换是绕不开的话题。ATL（Active Template Library）提供了一套简洁易用的字符串转换宏，其中 \u003Ccode>CW2A\u003C\u002Fcode> 和 \u003Ccode>CA2W\u003C\u002Fcode> 最为常用。本文从 Windows 字符编码历史讲起，深入解析这套转换机制。\u003C\u002Fp>\n\u003Ch2 id=\"windows-字符编码历史\">Windows 字符编码历史\u003C\u002Fh2>\n\u003Ch3 id=\"ansi-时代-win9x\">ANSI 时代（Win9x）\u003C\u002Fh3>\n\u003Cp>早期 Windows 使用 ANSI 字符集（实际是各地区的代码页，如 GBK、Shift-JIS 等）。API 函数有 \u003Ccode>A\u003C\u002Fcode> 后缀版本，如 \u003Ccode>CreateWindowA\u003C\u002Fcode>，接受 \u003Ccode>char*\u003C\u002Fcode> 参数。\u003C\u002Fp>\n\u003Ch3 id=\"unicode-时代-winnt-2000\">Unicode 时代（WinNT\u002F2000+）\u003C\u002Fh3>\n\u003Cp>Windows NT 内核原生使用 UTF-16LE（Windows 称之为&quot;Unicode&quot;）。API 函数有 \u003Ccode>W\u003C\u002Fcode> 后缀版本，如 \u003Ccode>CreateWindowW\u003C\u002Fcode>，接受 \u003Ccode>wchar_t*\u003C\u002Fcode> 参数。\u003C\u002Fp>\n\u003Cp>现代 Windows 推荐使用 Unicode（W 系列）API。当你包含 \u003Ccode>&lt;windows.h&gt;\u003C\u002Fcode> 并定义 \u003Ccode>UNICODE\u003C\u002Fcode> 宏时，\u003Ccode>CreateWindow\u003C\u002Fcode> 会自动映射到 \u003Ccode>CreateWindowW\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Ch3 id=\"字符类型对照\">字符类型对照\u003C\u002Fh3>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>类型\u003C\u002Fth>\n\u003Cth>宽度\u003C\u002Fth>\n\u003Cth>编码\u003C\u002Fth>\n\u003Cth>典型用途\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>char\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>1 字节\u003C\u002Ftd>\n\u003Ctd>ANSI\u002FUTF-8\u002FGBK\u003C\u002Ftd>\n\u003Ctd>窄字符，\u003Ccode>A\u003C\u002Fcode> 系列 API\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>wchar_t\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>2 字节（Windows）\u003C\u002Ftd>\n\u003Ctd>UTF-16LE\u003C\u002Ftd>\n\u003Ctd>宽字符，\u003Ccode>W\u003C\u002Fcode> 系列 API\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>TCHAR\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>依编译选项\u003C\u002Ftd>\n\u003Ctd>ANSI 或 Unicode\u003C\u002Ftd>\n\u003Ctd>兼容两者（已过时）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>CHAR\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>1 字节\u003C\u002Ftd>\n\u003Ctd>ANSI\u003C\u002Ftd>\n\u003Ctd>Win32 类型别名\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>WCHAR\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>2 字节\u003C\u002Ftd>\n\u003Ctd>UTF-16LE\u003C\u002Ftd>\n\u003Ctd>Win32 类型别名\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2 id=\"atl-转换宏原理\">ATL 转换宏原理\u003C\u002Fh2>\n\u003Cp>ATL 转换宏（定义在 \u003Ccode>&lt;atlconv.h&gt;\u003C\u002Fcode> 或 \u003Ccode>&lt;atlbase.h&gt;\u003C\u002Fcode>）利用 C++ 栈上的临时对象实现字符串转换。其核心原理：\u003C\u002Fp>\n\u003Col>\n\u003Cli>构造时分配转换缓冲区（小字符串用栈，大字符串用堆）\u003C\u002Fli>\n\u003Cli>调用 \u003Ccode>MultiByteToWideChar\u003C\u002Fcode> 或 \u003Ccode>WideCharToMultiByte\u003C\u002Fcode> 进行实际转换\u003C\u002Fli>\n\u003Cli>析构时自动释放堆内存（如果用了堆）\u003C\u002Fli>\n\u003Cli>提供隐式转换运算符，可直接当作指针使用\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>宏名称的命名规则：\u003Ccode>C{源编码}2{目标编码}[EX]\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>W\u003C\u002Fstrong> = Wide（宽字符，\u003Ccode>wchar_t*\u003C\u002Fcode>，UTF-16LE）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>A\u003C\u002Fstrong> = ANSI（窄字符，\u003Ccode>char*\u003C\u002Fcode>，当前 ANSI 代码页）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>T\u003C\u002Fstrong> = TCHAR（依 \u003Ccode>UNICODE\u003C\u002Fcode> 宏决定）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>U\u003C\u002Fstrong> = UTF-8（\u003Ccode>char*\u003C\u002Fcode>，UTF-8 编码）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>OLE\u003C\u002Fstrong> = OLESTR（\u003Ccode>OLECHAR*\u003C\u002Fcode>，通常等同于 \u003Ccode>WCHAR*\u003C\u002Fcode>）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>EX\u003C\u002Fstrong> 后缀 = 扩展版本，支持显式指定代码页\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"主要宏详解\">主要宏详解\u003C\u002Fh2>\n\u003Ch3 id=\"cw2a-wide-转-ansi\">CW2A — Wide 转 ANSI\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;atlconv.h&gt;\n\nvoid Example_CW2A() {\n    const wchar_t* wstr = L&quot;Hello, 世界&quot;;\n    \n    \u002F\u002F 基本用法：转为系统默认 ANSI 代码页（如 GBK）\n    CW2A narrow(wstr);\n    printf(&quot;ANSI: %s\n&quot;, (LPCSTR)narrow);  \u002F\u002F 或直接 printf(&quot;%s&quot;, narrow);\n    \n    \u002F\u002F 指定 UTF-8 代码页\n    CW2A utf8(wstr, CP_UTF8);\n    printf(&quot;UTF-8: %s\n&quot;, (LPCSTR)utf8);\n    \n    \u002F\u002F 用于需要 LPCSTR 的函数\n    SomeFunctionNeedingAnsi((LPCSTR)CW2A(wstr));\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"ca2w-ansi-转-wide\">CA2W — ANSI 转 Wide\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">void Example_CA2W() {\n    const char* astr = &quot;Hello World&quot;;\n    \n    \u002F\u002F 基本用法：从系统默认代码页转 UTF-16\n    CA2W wide(astr);\n    wprintf(L&quot;Wide: %s\n&quot;, (LPCWSTR)wide);\n    \n    \u002F\u002F 从 UTF-8 转 UTF-16（现代推荐方式）\n    const char* utf8str = u8&quot;Hello, 世界&quot;;\n    CA2W wideFromUtf8(utf8str, CP_UTF8);\n    wprintf(L&quot;From UTF-8: %s\n&quot;, (LPCWSTR)wideFromUtf8);\n    \n    \u002F\u002F 赋值给 CString\n    CStringW cstr = CA2W(astr);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"ct2a-和-ct2w-tchar-转换\">CT2A 和 CT2W — TCHAR 转换\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F CT2A: TCHAR -&gt; ANSI\n\u002F\u002F CT2W: TCHAR -&gt; Wide\n\u002F\u002F CA2T: ANSI -&gt; TCHAR\n\u002F\u002F CW2T: Wide -&gt; TCHAR\n\nvoid Example_TCHAR() {\n    LPCTSTR tstr = _T(&quot;Hello&quot;);\n    \n    CT2A ansi(tstr);           \u002F\u002F 转为 ANSI\n    CT2W wide(tstr);           \u002F\u002F 转为 Wide（若已是 Wide 则无操作）\n    \n    CA2T fromAnsi(&quot;Hello&quot;);    \u002F\u002F ANSI 转 TCHAR\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"cw2ca-ca2cw-const-版本\">CW2CA \u002F CA2CW — Const 版本\u003C\u002Fh3>\n\u003Cp>加 \u003Ccode>C\u003C\u002Fcode> 表示返回 \u003Ccode>const\u003C\u002Fcode> 指针版本，语义更安全：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">CW2CA  \u002F\u002F const LPSTR\nCA2CW  \u002F\u002F const LPWSTR\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>通常优先使用带 \u003Ccode>C\u003C\u002Fcode> 的版本，除非你确实需要修改转换结果。\u003C\u002Fp>\n\u003Ch3 id=\"完整宏列表\">完整宏列表\u003C\u002Fh3>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>宏\u003C\u002Fth>\n\u003Cth>源类型\u003C\u002Fth>\n\u003Cth>目标类型\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>CA2W\u003C\u002Fcode> \u002F \u003Ccode>CA2CW\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>char*\u003C\u002Fcode> (ANSI)\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>wchar_t*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>CW2A\u003C\u002Fcode> \u002F \u003Ccode>CW2CA\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>wchar_t*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>char*\u003C\u002Fcode> (ANSI)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>CA2T\u003C\u002Fcode> \u002F \u003Ccode>CA2CT\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>char*\u003C\u002Fcode> (ANSI)\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>TCHAR*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>CT2A\u003C\u002Fcode> \u002F \u003Ccode>CT2CA\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>TCHAR*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>char*\u003C\u002Fcode> (ANSI)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>CW2T\u003C\u002Fcode> \u002F \u003Ccode>CW2CT\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>wchar_t*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>TCHAR*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>CT2W\u003C\u002Fcode> \u002F \u003Ccode>CT2CW\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>TCHAR*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>wchar_t*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>CA2OLE\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>char*\u003C\u002Fcode> (ANSI)\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>OLECHAR*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>COLE2A\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>OLECHAR*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>char*\u003C\u002Fcode> (ANSI)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2 id=\"代码页参数\">代码页参数\u003C\u002Fh2>\n\u003Cp>\u003Ccode>EX\u003C\u002Fcode> 版本或第二参数允许指定代码页：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F 常用代码页\nCA2W(str, CP_ACP);    \u002F\u002F 系统默认 ANSI（通常 GBK in China）\nCA2W(str, CP_UTF8);   \u002F\u002F UTF-8（强烈推荐）\nCA2W(str, CP_OEMCP);  \u002F\u002F OEM 代码页（控制台）\nCA2W(str, 936);       \u002F\u002F 明确指定 GBK\nCA2W(str, 65001);     \u002F\u002F UTF-8（数字形式）\nCA2W(str, 1252);      \u002F\u002F Windows-1252（西欧）\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在现代 Windows 开发中，强烈建议：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>所有 \u003Ccode>char*\u003C\u002Fcode> 字符串使用 UTF-8 编码\u003C\u002Fli>\n\u003Cli>转换时明确指定 \u003Ccode>CP_UTF8\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>避免依赖系统默认代码页（\u003Ccode>CP_ACP\u003C\u002Fcode>），因为不同机器可能不同\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"与-widechartomultibyte-对比\">与 WideCharToMultiByte 对比\u003C\u002Fh2>\n\u003Cp>ATL 宏本质上是对 \u003Ccode>MultiByteToWideChar\u003C\u002Fcode> \u002F \u003Ccode>WideCharToMultiByte\u003C\u002Fcode> 的封装：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F 手动方式（WideCharToMultiByte）\nstd::string WideToUtf8(const std::wstring&amp; wide) {\n    if (wide.empty()) return &quot;&quot;;\n    \n    int size = WideCharToMultiByte(\n        CP_UTF8, 0,\n        wide.c_str(), (int)wide.size(),\n        nullptr, 0,\n        nullptr, nullptr\n    );\n    \n    std::string result(size, 0);\n    WideCharToMultiByte(\n        CP_UTF8, 0,\n        wide.c_str(), (int)wide.size(),\n        &amp;result[0], size,\n        nullptr, nullptr\n    );\n    return result;\n}\n\n\u002F\u002F ATL 方式（等价，但简洁得多）\nstd::string WideToUtf8_ATL(const std::wstring&amp; wide) {\n    CW2A utf8(wide.c_str(), CP_UTF8);\n    return std::string((LPCSTR)utf8);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>对比项\u003C\u002Fth>\n\u003Cth>ATL 宏\u003C\u002Fth>\n\u003Cth>WideCharToMultiByte\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>代码量\u003C\u002Ftd>\n\u003Ctd>极少\u003C\u002Ftd>\n\u003Ctd>较多（需两次调用）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>安全性\u003C\u002Ftd>\n\u003Ctd>高（RAII）\u003C\u002Ftd>\n\u003Ctd>需手动管理缓冲\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>灵活性\u003C\u002Ftd>\n\u003Ctd>中\u003C\u002Ftd>\n\u003Ctd>高（更多控制标志）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>依赖\u003C\u002Ftd>\n\u003Ctd>需要 ATL\u003C\u002Ftd>\n\u003Ctd>仅 Win32\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>错误处理\u003C\u002Ftd>\n\u003Ctd>有限\u003C\u002Ftd>\n\u003Ctd>可通过 GetLastError\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>性能\u003C\u002Ftd>\n\u003Ctd>略有封装开销\u003C\u002Ftd>\n\u003Ctd>略高\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2 id=\"常见坑点\">常见坑点\u003C\u002Fh2>\n\u003Ch3 id=\"坑-1-临时对象生命周期\">坑 1：临时对象生命周期\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F ❌ 危险！CW2A 是临时对象，函数返回后 LPCSTR 悬空\nLPCSTR GetNarrow(const wchar_t* wide) {\n    return CW2A(wide);  \u002F\u002F 返回指向已销毁临时对象的指针！\n}\n\n\u002F\u002F ✅ 正确：保存对象，再返回指针\nvoid UseNarrow(const wchar_t* wide) {\n    CW2A narrow(wide);\n    UseString((LPCSTR)narrow);  \u002F\u002F 在 narrow 的生命周期内使用\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"坑-2-nullptr-输入\">坑 2：nullptr 输入\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F ❌ 可能崩溃\nconst wchar_t* wstr = nullptr;\nCW2A narrow(wstr);  \u002F\u002F ATL 宏通常不处理 nullptr\n\n\u002F\u002F ✅ 先检查\nif (wstr) {\n    CW2A narrow(wstr);\n    \u002F\u002F ...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"坑-3-大字符串的隐式堆分配\">坑 3：大字符串的隐式堆分配\u003C\u002Fh3>\n\u003Cp>ATL 转换宏默认使用固定大小的栈缓冲区（通常 128 字节），超出后切换到堆。这是自动的，但要注意：如果在循环中频繁转换大字符串，性能可能不如直接使用 \u003Ccode>WideCharToMultiByte\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F 对于大量数据，直接用 Win32 API 或标准库更好\nfor (auto&amp; item : largeList) {\n    \u002F\u002F ⚠️ 可能频繁堆分配\n    CW2A narrow(item.c_str());\n    \u002F\u002F ...\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"坑-4-不要修改转换后的内容\">坑 4：不要修改转换后的内容\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">CW2CA narrow(wstr);  \u002F\u002F const 版本\n\u002F\u002F LPSTR p = narrow;  \u002F\u002F 编译错误（好事！）\nLPCSTR p = narrow;   \u002F\u002F OK\n\nCW2A narrow2(wstr);  \u002F\u002F 非 const 版本\nLPSTR p2 = narrow2;  \u002F\u002F 可以修改，但通常不推荐\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"坑-5-代码页不一致\">坑 5：代码页不一致\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F 文件以 GBK 保存，但用 UTF-8 解析 → 乱码\nCA2W w1(gbkStr, CP_UTF8);  \u002F\u002F ❌ 错误的代码页\n\n\u002F\u002F 明确指定正确的代码页\nCA2W w2(gbkStr, 936);       \u002F\u002F ✅ GBK\nCA2W w3(utf8Str, CP_UTF8);  \u002F\u002F ✅ UTF-8\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"最佳实践\">最佳实践\u003C\u002Fh2>\n\u003Ch3 id=\"1-优先使用-unicode-w-系列-api\">1. 优先使用 Unicode（W 系列）API\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F 现代 Windows 开发推荐\n#define UNICODE\n#define _UNICODE\n#include &lt;windows.h&gt;\n\n\u002F\u002F 这样 CreateWindow 就是 CreateWindowW\nCreateWindow(L&quot;MyClass&quot;, L&quot;Title&quot;, ...);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"2-内部统一使用-std-wstring-或-utf-8-std-string\">2. 内部统一使用 std::wstring 或 UTF-8 std::string\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F 方案A：全程 wstring（Windows 优先）\nstd::wstring title = L&quot;应用标题&quot;;\nSetWindowTextW(hwnd, title.c_str());\n\n\u002F\u002F 方案B：全程 UTF-8 string（跨平台友好）\nstd::string title_utf8 = u8&quot;应用标题&quot;;\nCA2W title_wide(title_utf8.c_str(), CP_UTF8);\nSetWindowTextW(hwnd, title_wide);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"3-边界处理函数封装\">3. 边界处理函数封装\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F 推荐的转换工具函数\nstd::wstring Utf8ToWide(const std::string&amp; utf8) {\n    if (utf8.empty()) return L&quot;&quot;;\n    CA2W wide(utf8.c_str(), CP_UTF8);\n    return std::wstring((LPCWSTR)wide);\n}\n\nstd::string WideToUtf8(const std::wstring&amp; wide) {\n    if (wide.empty()) return &quot;&quot;;\n    CW2A utf8(wide.c_str(), CP_UTF8);\n    return std::string((LPCSTR)utf8);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"4-c-17-以上考虑使用标准库\">4. C++17 以上考虑使用标准库\u003C\u002Fh3>\n\u003Cp>C++17 引入了 \u003Ccode>&lt;filesystem&gt;\u003C\u002Fcode>，C++20 引入了 \u003Ccode>&lt;format&gt;\u003C\u002Fcode>。对于跨平台项目，可以使用第三方库如 \u003Ccode>utfcpp\u003C\u002Fcode> 或 \u003Ccode>icu\u003C\u002Fcode>，减少对 ATL 的依赖。\u003C\u002Fp>\n\u003Cp>ATL 的字符串转换宏虽然简单，但理解其原理和陷阱，才能在 Windows 开发中正确无误地处理字符编码。\u003C\u002Fp>\n","2023-06-09",[11,12,13,14],"cpp","windows","ATL","字符串",false,[17,30,41,53,63,70,77,84,91,98,108,117,127,136,144,152,161,170,179,189,196,205,211,218,224,233,240,247,255,265,274,283,293,303,313,321,331,342,345,354,362,368,376,384,392,400,408,415],{"slug":18,"title":19,"description":20,"pub_date":21,"tags":22,"draft":15,"word_count":29},"ide-skills-guide","Agent Skills 完全指南：21 款第三方 Skill 深度评测与使用心得","全面评测 21 款第三方 Agent Skills，涵盖 Vue 生态、前端设计、构建工具、实用工具四大分类。从安装配置到实际使用场景，带你了解每个 Skill 的功能特点、最佳实践与使用心得。","2026-06-15",[23,24,25,26,27,28],"agent","skills","AI","效率工具","前端","Vue",4169,{"slug":31,"title":32,"description":33,"pub_date":34,"tags":35,"draft":15,"word_count":40},"linux-kernel-skeleton-struct-funcptr-container_of","Linux 内核骨架：struct、函数指针与 container_of","读懂 Linux 内核源码的三件套：巨大的 struct 组合代替继承、函数指针表实现虚派发、container_of 宏从嵌入成员找回完整对象。","2026-05-09",[36,37,38,39],"linux","kernel","C","container_of",1369,{"slug":42,"title":43,"description":44,"pub_date":45,"tags":46,"draft":15,"word_count":52},"astro-complete-guide-2025","Astro 5 深度剖析：Islands 架构原理、构建优化与 Cloudflare Workers 边缘部署","从编译器视角解析 Astro 5 的 Islands 架构实现原理，Content Layer API 的 Vite 插件机制，Server Islands 的流式渲染，以及如何在 Cloudflare Workers + D1 边缘环境下榨干性能。","2026-05-08",[47,48,49,50,51],"astro","frontend","cloudflare","performance","architecture",3663,{"slug":54,"title":55,"description":56,"pub_date":57,"tags":58,"draft":15,"word_count":62},"llm-prompt-engineering","Prompt Engineering 实战：让 LLM 真正听话的技巧","System prompt 怎么写、Few-shot 怎么设计、Chain-of-Thought 原理，以及常见失败模式和调试方法。","2026-05-03",[59,60,61],"ai","llm","工程实践",1723,{"slug":64,"title":65,"description":66,"pub_date":57,"tags":67,"draft":15,"word_count":69},"rag-system-design","RAG 系统设计：从 naive 到 production-ready","Retrieval-Augmented Generation 不只是「向量数据库 + LLM」，分块策略、召回质量、重排序、缓存才是工程核心。",[59,68,60,61],"rag",1613,{"slug":71,"title":72,"description":73,"pub_date":57,"tags":74,"draft":15,"word_count":76},"git-advanced-workflow","Git 进阶工作流：rebase、cherry-pick、bisect 的正确使用","merge 会了，但 rebase 总搞错？bisect 找 bug 提交？interactive rebase 整理历史？这篇一次说清楚。",[75,61],"git",1396,{"slug":78,"title":79,"description":80,"pub_date":57,"tags":81,"draft":15,"word_count":83},"docker-practical-guide","Docker 实战：从会用到用好","会 docker run 不够，Dockerfile 最佳实践、多阶段构建、Compose 编排、镜像瘦身才是日常真正需要的。",[82,36,61],"docker",1268,{"slug":85,"title":86,"description":87,"pub_date":57,"tags":88,"draft":15,"word_count":90},"anthropics-skills-guide","anthropics\u002Fskills：Anthropic 官方 Agent Skills 仓库解析","Anthropic 官方开源的 Agent Skills 标准仓库，127k stars，解析 SKILL.md 规范、17 个示例 skill 的设计模式，以及如何在 Claude Code \u002F Claude.ai \u002F API 中使用",[59,89,23,24],"Claude",2090,{"slug":92,"title":93,"description":94,"pub_date":57,"tags":95,"draft":15,"word_count":97},"karpathy-claude-code-guidelines","Karpathy 的 LLM 编码批评与 CLAUDE.md 最佳实践","基于 Andrej Karpathy 对 LLM 编程助手的观察，forrestchang 提炼出一个 CLAUDE.md 文件，4 条原则解决 AI 编码的典型失控问题：乱猜假设、过度设计、乱改代码、目标不清",[59,89,96,61],"Claude Code",2699,{"slug":99,"title":100,"description":101,"pub_date":57,"tags":102,"draft":15,"word_count":107},"typescript-advanced-patterns","TypeScript 高级模式：让类型系统为你工作","基础 TS 会了但类型总是 any？条件类型、映射类型、模板字面量类型、infer 关键字才是 TS 的真正威力。",[103,104,105,106],"typescript","类型系统","前端工程","高级模式",1419,{"slug":109,"title":110,"description":111,"pub_date":57,"tags":112,"draft":15,"word_count":116},"linux-performance-tuning","Linux 性能调优实战：从 top 到 perf 的完整工具链","遇到性能问题不知道从哪下手？这篇建立系统化的排查思路，从 CPU\u002F内存\u002FIO\u002F网络逐层分析。",[36,113,114,115],"性能","运维","系统编程",1524,{"slug":118,"title":119,"description":120,"pub_date":57,"tags":121,"draft":15,"word_count":126},"python-functional-programming","Python 函数式编程：map\u002Ffilter\u002Freduce 之外","Python 不是纯函数式语言，但 functools、itertools、偏函数、闭包这些工具用好了能让代码简洁一个量级。",[122,123,124,125],"python","函数式","闭包","装饰器",1867,{"slug":128,"title":129,"description":130,"pub_date":57,"tags":131,"draft":15,"word_count":135},"python-oop-guide","Python 面向对象：__init__ 之外你需要知道的","Python OOP 不只是 class + __init__，魔术方法、描述符、元类才是真正的武器。",[122,132,133,134],"OOP","面向对象","魔术方法",1792,{"slug":137,"title":138,"description":139,"pub_date":57,"tags":140,"draft":15,"word_count":143},"python-data-structures","Python 内置数据结构深度解析","list、dict、set、tuple 不只是数据容器，搞懂它们的底层实现和时间复杂度，才能写出高性能 Python。",[122,141,113,142],"数据结构","算法",1517,{"slug":145,"title":146,"description":147,"pub_date":57,"tags":148,"draft":15,"word_count":151},"python-basics-quick-start","Python 快速上手：写给有编程基础的人","已经会其他语言，想快速掌握 Python 的语法特性和思维方式，这篇是捷径。",[122,149,150],"入门","基础",1607,{"slug":153,"title":154,"description":155,"pub_date":57,"tags":156,"draft":15,"word_count":160},"python-dataclass-pydantic","Python dataclass vs Pydantic：数据类选型指南","dataclass 是标准库的轻量选择，Pydantic v2 是带验证的重武器，什么时候用哪个，这篇说清楚。",[122,157,158,159],"dataclass","pydantic","数据验证",1323,{"slug":162,"title":163,"description":164,"pub_date":57,"tags":165,"draft":15,"word_count":169},"python-asyncio-practical","Python asyncio 实战：从回调地狱到协程优雅","asyncio 是 Python 异步编程的核心，搞懂 event loop、Task、gather 这些概念才能写出真正高效的异步代码。",[122,166,167,168],"asyncio","并发","网络编程",1258,{"slug":171,"title":172,"description":173,"pub_date":57,"tags":174,"draft":15,"word_count":178},"python-type-hints-guide","Python 类型注解完全指南：从入门到实践","Python 3.5+ 引入类型注解，配合 mypy\u002Fpyright 让 Python 也能享受静态类型检查的好处。",[122,175,176,177],"typescript-style","type-hints","工具链",1102,{"slug":180,"title":181,"description":182,"pub_date":183,"tags":184,"draft":15,"word_count":188},"pwa-install-update-button","PWA 踩坑：为什么安装按钮从来不出现","从 beforeinstallprompt 到 Service Worker waiting，把 PWA 的安装与更新提示真正做对","2026-05-02",[185,186,187],"pwa","javascript","web",1683,{"slug":190,"title":191,"description":192,"pub_date":193,"tags":194,"draft":15,"word_count":195},"openclaw-vs-hermes-agent","OpenClaw vs Hermes Agent：两个本地优先 Agent 的设计差异","OpenClaw（Novita AI）和 Hermes Agent（Nous Research）都是本地运行的个人 AI Agent，但在记忆系统、技能学习、运行环境和模型生态上走了不同的路。深入对比两种架构的核心差异。","2026-05-01",[59,23,60],1679,{"slug":197,"title":198,"description":199,"pub_date":193,"tags":200,"draft":15,"word_count":204},"cpp-random-design-patterns","C++ 设计模式实战：RAII、观察者、工厂","用现代 C++（C++17\u002F20）实现三种高频设计模式：RAII 资源管理、观察者模式事件系统、工厂模式插件架构。每种模式给出问题场景、实现代码和真实工程案例。",[11,201,202,203],"设计模式","c++17","工程",2613,{"slug":206,"title":207,"description":208,"pub_date":193,"tags":209,"draft":15,"word_count":210},"data-structures-fundamentals","数据结构基础：从数组到红黑树","系统梳理常用数据结构的核心原理、时间复杂度和适用场景。数组、链表、栈、队列、哈希表、二叉树、堆、图，每种结构附实现要点和 C++ 代码片段。",[141,142,11,150],3004,{"slug":212,"title":213,"description":214,"pub_date":215,"tags":216,"draft":15,"word_count":217},"ai-agent-what-is","什么是 AI Agent？从 LLM 到自主执行","LLM 本身是无状态问答机，Agent 是什么让它’动’起来的？本文深入解析 Agent 的四个核心能力、ReAct 框架、工具调用原理，以及主流框架横向对比。","2026-04-30",[59,23,60],2116,{"slug":219,"title":220,"description":221,"pub_date":215,"tags":222,"draft":15,"word_count":223},"ai-agent-memory","AI Agent 的记忆系统：从上下文窗口到长期记忆","深入拆解 AI Agent 的四种记忆类型、上下文窗口压缩策略、RAG 向量检索原理，以及三种典型失败模式和工程选型建议。",[59,23,68],2052,{"slug":225,"title":226,"description":227,"pub_date":215,"tags":228,"draft":15,"word_count":232},"network-proxy-vpn-guide","代理与翻墙技术原理：从 HTTP 代理到现代协议","深入解析代理与 VPN 的本质区别，梳理从 SOCKS5 到 Shadowsocks、V2Ray\u002FXray、Hysteria2 的协议演进，以及机场订阅的技术本质。",[229,230,231],"网络","代理","协议",2148,{"slug":234,"title":235,"description":236,"pub_date":215,"tags":237,"draft":15,"word_count":151},"algorithm-binary-search","二分查找：永远写不对？记住这个模板","彻底搞清楚二分查找的边界问题：闭区间和左闭右开两套模板、三道经典 LeetCode 题目完整 C++ 实现，以及二分答案的进阶思路。",[142,238,239,11],"二分查找","leetcode",{"slug":241,"title":242,"description":243,"pub_date":215,"tags":244,"draft":15,"word_count":246},"algorithm-sliding-window","滑动窗口算法：从暴力到 O(n) 的思维跃迁","系统讲解滑动窗口算法的核心模板、适用题型，配合三道经典 LeetCode 题目的完整 C++ 实现，彻底理解双指针收缩思路。",[142,245,239,11],"滑动窗口",1943,{"slug":248,"title":249,"description":250,"pub_date":215,"tags":251,"draft":15,"word_count":254},"network-clash-config","Clash \u002F Mihomo 配置详解：规则、策略组与分流","深入解析 Clash\u002FMihomo 的核心配置结构，包括代理节点、策略组类型、规则优先级、DNS fake-ip 模式，以及一份实用的完整配置模板。",[229,252,230,253],"clash","配置",1292,{"slug":256,"title":257,"description":258,"pub_date":259,"tags":260,"draft":15,"word_count":264},"hid-hotplug","HID 设备热插拔检测：从 udev 到 node-hid","在 Linux 上用 node-hid + usb 库实现可靠的 USB HID 设备热插拔检测，踩坑记录","2026-04-28",[11,261,36,262,263],"hid","nodejs","electron",2039,{"slug":266,"title":267,"description":268,"pub_date":269,"tags":270,"draft":15,"word_count":273},"electron-ipc-types","Electron IPC 类型安全：从 any 到完全类型化","用 TypeScript 泛型封装 Electron IPC，彻底消灭 any，preload 契约集中管理","2026-04-25",[263,103,271,272],"ipc","vue",1446,{"slug":275,"title":276,"description":277,"pub_date":278,"tags":279,"draft":15,"word_count":282},"element-plus-popover-hide","手动关闭多个 el-popover（不用 v-model:visible）","通过 ref + Reflect.get 调用 hide() 方法手动关闭 Element Plus Popover，解释 Vue3 Proxy 导致无法直接调用实例方法的原因。","2024-10-25",[272,280,281],"element-plus","vue3",1321,{"slug":284,"title":285,"description":286,"pub_date":287,"tags":288,"draft":15,"word_count":292},"vite-vue3-ts-elementplus-pinia","用 Vite+（vp）从零搭建 Vue3 + TypeScript + Element Plus + Pinia + Vue Router","使用 Vite+ 统一工具链（vp）一条命令搭建 Vue3 全家桶，涵盖按需导入、Pinia store、路由配置，以及常见坑的解决方案。","2024-08-27",[272,289,103,280,290,291],"vite","pinia","vite-plus",1960,{"slug":294,"title":295,"description":296,"pub_date":297,"tags":298,"draft":15,"word_count":302},"cef-lnk2038-iterator-debug-level","CEF LNK2038：解决 _ITERATOR_DEBUG_LEVEL 不匹配错误","分析 CEF（Chromium Embedded Framework）集成时出现的 LNK2038 _ITERATOR_DEBUG_LEVEL 链接错误，从根本原因到解决方案的完整指南。","2024-05-07",[11,299,300,301],"CEF","Visual Studio","链接错误",1509,{"slug":304,"title":305,"description":306,"pub_date":307,"tags":308,"draft":15,"word_count":312},"npm-electron-install-fix","彻底解决 npm 安装 Electron 失败的问题","分析 npm install electron 失败的根本原因（下载二进制超时\u002F被墙），通过国内镜像（npmmirror）彻底解决，并介绍多种备选方案和常见错误排查。","2024-03-01",[263,309,310,311],"npm","前端工具链","国内镜像",1494,{"slug":314,"title":315,"description":316,"pub_date":317,"tags":318,"draft":15,"word_count":320},"git-out-of-memory","解决 git 报错：Fatal: Out of memory, malloc failed","分析 git 大仓库操作时出现 Out of memory malloc failed 的根本原因，通过调整 pack.windowMemory、http.postBuffer 和 git repack 彻底解决。","2024-01-31",[75,36,319],"工具",2244,{"slug":322,"title":323,"description":324,"pub_date":325,"tags":326,"draft":15,"word_count":330},"vmware-tools-install","在 VMware 虚拟机中安装 open-vm-tools 完整指南","详解 VMware Tools 的作用、open-vm-tools 与官方 VMware Tools 的区别，以及在 Ubuntu 虚拟机中安装并生效的完整步骤和常见问题排查。","2023-11-21",[327,36,328,329],"VMware","Ubuntu","虚拟机",2523,{"slug":332,"title":333,"description":334,"pub_date":335,"tags":336,"draft":15,"word_count":341},"load-balancing-algorithms","负载均衡算法完全指南：从轮询到一致性哈希","系统梳理静态与动态负载均衡算法，涵盖轮询、随机、权重、IP Hash、一致性 Hash、最少连接、最快响应等，并对比 Nginx、Dubbo、Spring Cloud LoadBalancer 的实现差异。","2023-11-15",[337,338,339,340],"分布式","负载均衡","Nginx","微服务",1764,{"slug":4,"title":5,"description":6,"pub_date":9,"tags":343,"draft":15,"word_count":344},[11,12,13,14],1665,{"slug":346,"title":347,"description":348,"pub_date":9,"tags":349,"draft":15,"word_count":353},"csharp-sendmessage-cpp","C# 通过 SendMessage 向 C++ 窗口发送消息与字符串","使用 P\u002FInvoke 调用 user32.dll 的 SendMessage，从 C# 发送自定义 WM_USER 消息及字符串指针给 C++ 原生窗口，并在 C++ 侧正确接收和转换。",[350,11,12,351,352],"C#","互操作","PInvoke",1554,{"slug":355,"title":356,"description":357,"pub_date":358,"tags":359,"draft":15,"word_count":361},"win-postmessage-vector","Windows PostMessage 跨线程传递 std::vector 指针","通过 PostMessage 在 Windows 消息队列中传递 std::vector 指针，使用 reinterpret_cast 将指针装入 LPARAM，并在接收方正确释放内存。","2023-05-26",[11,12,360],"WinAPI",1823,{"slug":363,"title":364,"description":365,"pub_date":358,"tags":366,"draft":15,"word_count":367},"exe-dll-single-package","将 EXE 和 DLL 打包成单一可执行文件","介绍两种将 exe 和依赖 dll 打包成单文件的方案：Enigma Virtual Box 和 WinRAR 自解压，适合发布 Windows 桌面程序时简化分发流程。",[12,11,319],1619,{"slug":369,"title":370,"description":371,"pub_date":358,"tags":372,"draft":15,"word_count":375},"cpp-random-mt19937","C++ 现代随机数生成：用 mt19937 彻底告别 rand()","深入讲解为什么 rand() 不够用，以及如何用 C++11 的 \u003Crandom> 库正确生成高质量随机数，涵盖 mt19937、各种分布和线程安全。",[11,373,374],"c++11","random",1549,{"slug":377,"title":378,"description":379,"pub_date":380,"tags":381,"draft":15,"word_count":383},"win-startup-registry","C++ 实现程序开机自启动：注册表方式详解","通过操作 Windows 注册表 Run 键实现程序开机自启动，包括 HKCU 与 HKLM 区别、完整封装代码、工作目录问题和 UAC 权限处理。","2022-12-26",[12,11,382],"registry",1201,{"slug":385,"title":386,"description":387,"pub_date":388,"tags":389,"draft":15,"word_count":391},"mfc-cstring-wparam","MFC 中 CString 与 WPARAM 之间的转换","详解 MFC 消息传递中 CString 无法直接强转为 WPARAM 的原因，以及两种正确的转换方案，并介绍结构体指针传递的正确姿势。","2022-11-25",[390,11,12],"mfc",1546,{"slug":393,"title":394,"description":395,"pub_date":396,"tags":397,"draft":15,"word_count":399},"duilib-static-build","正确编译 Duilib 静态库：避免 ATL 依赖和链接错误","详解如何用 DuiLib_Static.vcxproj 编译 Duilib 静态库，解决 VARIANT 未定义、Unicode 配置不匹配和 ATL 依赖等常见问题。","2022-08-24",[11,398,12,390],"duilib",2639,{"slug":401,"title":402,"description":403,"pub_date":404,"tags":405,"draft":15,"word_count":407},"mfc-dpi-adaptive","MFC 界面自适应不同分辨率","MFC 对话框程序实现控件和字体随分辨率自动缩放的完整方案，附 DPI Awareness 配置说明","2022-08-17",[390,11,12,406],"dpi",1414,{"slug":409,"title":410,"description":411,"pub_date":412,"tags":413,"draft":15,"word_count":414},"mfc-drag-window","MFC 无标题栏窗口客户区拖动：三种方法对比","MFC 对话框去掉标题栏后如何实现拖动移动窗口，三种方案完整实现与适用场景分析","2022-08-16",[390,11,12],1633,{"slug":416,"title":417,"description":418,"pub_date":419,"tags":420,"draft":15,"word_count":422},"algorithm-number-complement","整数的补数：位运算掩码解法","LeetCode 476 题，用掩码 XOR 实现整数补数，附 C++\u002FPython\u002FJava 三种实现及补数与补码的区别","2021-03-08",[142,421,239],"位运算",1374,[]]