[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fKxvRw8FMx_wi5xM0XvUHaZSP9VtgPcsqtVrCj4F07Mw":3,"$fJU-4tot_gC5fDkujNeoE-cGsdMy5V_KcdUXLuAnTFgw":17,"$fYhUc2jL3Gsxl9UHkn64PFS-VuUmHzvcZH71XbxgaDDk":423},{"slug":4,"title":5,"description":6,"content":7,"content_html":8,"pub_date":9,"tags":10,"draft":16},"csharp-sendmessage-cpp","C# 通过 SendMessage 向 C++ 窗口发送消息与字符串","使用 P\u002FInvoke 调用 user32.dll 的 SendMessage，从 C# 发送自定义 WM_USER 消息及字符串指针给 C++ 原生窗口，并在 C++ 侧正确接收和转换。","# C# 通过 SendMessage 向 C++ 窗口发送消息与字符串\n\n在实际项目中，经常需要让 C# 程序与已有的 C++ 程序进行通信。基于 Windows 消息机制的进程间通信（IPC）是一种轻量、无需额外框架的方案。本文详细介绍如何用 C# 向 C++ 窗口发送消息和字符串数据。\n\n## 跨语言 IPC 背景\n\nWindows 进程间通信有多种方式：\n\n| 方式 | 优点 | 缺点 |\n|------|------|------|\n| Windows 消息（WM_COPYDATA） | 简单，无额外依赖 | 仅限桌面应用，需要窗口 |\n| 命名管道 | 双向，高吞吐 | 较复杂 |\n| 共享内存 | 最快 | 需要同步机制 |\n| COM\u002FDCOM | 标准化，功能强 | 注册复杂，重量级 |\n| 套接字（localhost） | 跨语言通用 | 有网络开销 |\n| 文件\u002F注册表 | 简单 | 慢，需要轮询 |\n\n对于简单的\"C# 控制 C++ 程序\"场景，`FindWindow` + `WM_COPYDATA`（或自定义消息）是最快的实现路径。\n\n## FindWindow\u002FFindWindowEx 定位目标窗口\n\n在发送消息前，需要获取目标窗口句柄：\n\n```csharp\nusing System;\nusing System.Runtime.InteropServices;\n\npublic class Win32Helper\n{\n    \u002F\u002F P\u002FInvoke 声明\n    [DllImport(\"user32.dll\", SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);\n    \n    [DllImport(\"user32.dll\", SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern IntPtr FindWindowEx(\n        IntPtr hwndParent, \n        IntPtr hwndChildAfter, \n        string lpszClass, \n        string lpszWindow);\n    \n    [DllImport(\"user32.dll\")]\n    public static extern bool IsWindow(IntPtr hWnd);\n}\n\n\u002F\u002F 使用\npublic class MessageSender\n{\n    public IntPtr FindTargetWindow()\n    {\n        \u002F\u002F 按窗口标题查找\n        IntPtr hwnd = Win32Helper.FindWindow(null, \"My C++ App\");\n        \n        if (hwnd == IntPtr.Zero)\n        {\n            int error = Marshal.GetLastWin32Error();\n            Console.WriteLine($\"FindWindow failed, error: {error}\");\n            return IntPtr.Zero;\n        }\n        \n        \u002F\u002F 验证窗口有效\n        if (!Win32Helper.IsWindow(hwnd))\n        {\n            Console.WriteLine(\"Handle is no longer valid\");\n            return IntPtr.Zero;\n        }\n        \n        return hwnd;\n    }\n    \n    public IntPtr FindChildWindow(IntPtr hwndParent, string childClass)\n    {\n        \u002F\u002F 查找子窗口（如特定控件）\n        return Win32Helper.FindWindowEx(hwndParent, IntPtr.Zero, childClass, null);\n    }\n}\n```\n\n## WM_COPYDATA 传递字符串\n\n`WM_COPYDATA` 是 Windows 专门为跨进程传递数据设计的消息，系统会自动处理内存映射。\n\n### C# 发送端\n\n```csharp\nusing System;\nusing System.Runtime.InteropServices;\nusing System.Text;\n\n[StructLayout(LayoutKind.Sequential)]\npublic struct COPYDATASTRUCT\n{\n    public IntPtr dwData;    \u002F\u002F 用户自定义的数据标识\n    public int cbData;       \u002F\u002F lpData 指向的数据大小（字节）\n    public IntPtr lpData;    \u002F\u002F 指向数据的指针\n}\n\npublic class CopyDataSender\n{\n    [DllImport(\"user32.dll\", SetLastError = true)]\n    private static extern IntPtr SendMessage(\n        IntPtr hWnd, \n        uint Msg, \n        IntPtr wParam, \n        ref COPYDATASTRUCT lParam);\n    \n    private const uint WM_COPYDATA = 0x004A;\n    private const int DATA_TYPE_STRING = 1;  \u002F\u002F 自定义的数据类型标识\n    \n    public bool SendString(IntPtr hwndTarget, IntPtr hwndSelf, string message)\n    {\n        if (hwndTarget == IntPtr.Zero || string.IsNullOrEmpty(message))\n            return false;\n        \n        \u002F\u002F 将 C# string 转为 UTF-16 字节（与 C++ wchar_t* 兼容）\n        byte[] bytes = Encoding.Unicode.GetBytes(message + \"\\0\");  \u002F\u002F 包含 null 终止符\n        \n        \u002F\u002F 在非托管内存中分配，避免 GC 移动\n        IntPtr pData = Marshal.AllocHGlobal(bytes.Length);\n        try\n        {\n            Marshal.Copy(bytes, 0, pData, bytes.Length);\n            \n            COPYDATASTRUCT cds = new COPYDATASTRUCT\n            {\n                dwData = new IntPtr(DATA_TYPE_STRING),\n                cbData = bytes.Length,\n                lpData = pData\n            };\n            \n            IntPtr result = SendMessage(hwndTarget, WM_COPYDATA, hwndSelf, ref cds);\n            return result != IntPtr.Zero;\n        }\n        finally\n        {\n            Marshal.FreeHGlobal(pData);  \u002F\u002F 必须释放\n        }\n    }\n}\n```\n\n### C++ 接收端\n\n```cpp\n#define WM_COPYDATA 0x004A\n\nLRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\n{\n    switch (msg)\n    {\n    case WM_COPYDATA:\n    {\n        COPYDATASTRUCT* pCds = reinterpret_cast\u003CCOPYDATASTRUCT*>(lParam);\n        \n        if (pCds->dwData == 1)  \u002F\u002F DATA_TYPE_STRING\n        {\n            \u002F\u002F lpData 是 UTF-16 字符串（C# 的 Unicode 编码）\n            const wchar_t* wstr = reinterpret_cast\u003Cconst wchar_t*>(pCds->lpData);\n            int charCount = pCds->cbData \u002F sizeof(wchar_t) - 1;  \u002F\u002F 减去 null 终止符\n            \n            std::wstring received(wstr, charCount);\n            \n            \u002F\u002F 使用接收到的字符串\n            MessageBoxW(hwnd, received.c_str(), L\"Received\", MB_OK);\n        }\n        \n        return TRUE;  \u002F\u002F 告诉发送方消息已处理\n    }\n    \u002F\u002F ...\n    }\n    return DefWindowProc(hwnd, msg, wParam, lParam);\n}\n```\n\n## 自定义 WM_APP 消息传递简单数值\n\n对于简单的控制命令（不需要传字符串），使用 `WM_APP` 范围的自定义消息更简单：\n\n```csharp\n\u002F\u002F C# 发送端\n[DllImport(\"user32.dll\", SetLastError = true)]\nprivate static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);\n\n[DllImport(\"user32.dll\", SetLastError = true)]\nprivate static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);\n\n\u002F\u002F 自定义消息定义（需与 C++ 端一致）\nprivate const uint WM_APP_CMD_PLAY  = 0x8000 + 1;  \u002F\u002F WM_APP = 0x8000\nprivate const uint WM_APP_CMD_PAUSE = 0x8000 + 2;\nprivate const uint WM_APP_CMD_SEEK  = 0x8000 + 3;\n\npublic void SendCommand(IntPtr hwndTarget, int command, int param = 0)\n{\n    PostMessage(hwndTarget, WM_APP_CMD_PLAY + (uint)command, \n                new IntPtr(param), IntPtr.Zero);\n}\n\n\u002F\u002F 求返回值时用 SendMessage\npublic int QueryStatus(IntPtr hwndTarget)\n{\n    IntPtr result = SendMessage(hwndTarget, WM_APP_CMD_SEEK, IntPtr.Zero, IntPtr.Zero);\n    return result.ToInt32();\n}\n```\n\n```cpp\n\u002F\u002F C++ 接收端\n#define WM_APP_CMD_PLAY  (WM_APP + 1)\n#define WM_APP_CMD_PAUSE (WM_APP + 2)\n#define WM_APP_CMD_SEEK  (WM_APP + 3)\n\ncase WM_APP_CMD_PLAY:\n    StartPlayback();\n    return 1;\n\ncase WM_APP_CMD_PAUSE:\n    PausePlayback();\n    return 1;\n\ncase WM_APP_CMD_SEEK:\n    return GetCurrentPosition();  \u002F\u002F 返回给 SendMessage 调用方\n```\n\n## P\u002FInvoke 声明完整参考\n\n```csharp\nusing System;\nusing System.Runtime.InteropServices;\nusing System.Text;\n\npublic static class User32\n{\n    \u002F\u002F 基本消息发送\n    [DllImport(\"user32.dll\", SetLastError = true)]\n    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, \n                                             IntPtr wParam, IntPtr lParam);\n    \n    [DllImport(\"user32.dll\", SetLastError = true)]\n    public static extern bool PostMessage(IntPtr hWnd, uint Msg, \n                                          IntPtr wParam, IntPtr lParam);\n    \n    \u002F\u002F WM_COPYDATA 专用重载\n    [DllImport(\"user32.dll\", SetLastError = true)]\n    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, \n                                             IntPtr wParam, ref COPYDATASTRUCT lParam);\n    \n    \u002F\u002F 字符串版本（用于 WM_GETTEXT 等）\n    [DllImport(\"user32.dll\", SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern int SendMessage(IntPtr hWnd, uint Msg, \n                                          int wParam, StringBuilder lParam);\n    \n    \u002F\u002F 窗口查找\n    [DllImport(\"user32.dll\", SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);\n    \n    [DllImport(\"user32.dll\", SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,\n                                              string lpszClass, string lpszWindow);\n    \n    \u002F\u002F 消息常量\n    public const uint WM_COPYDATA  = 0x004A;\n    public const uint WM_SETTEXT   = 0x000C;\n    public const uint WM_GETTEXT   = 0x000D;\n    public const uint WM_APP       = 0x8000;\n}\n```\n\n## 字符编码对齐（UTF-16）\n\nC# 的 `string` 和 C++ 的 `std::wstring`\u002F`wchar_t*` 都使用 UTF-16LE，天然兼容。\n\n```csharp\n\u002F\u002F C# 端：string → UTF-16 字节\nstring msg = \"Hello, 世界\";\nbyte[] utf16Bytes = Encoding.Unicode.GetBytes(msg);  \u002F\u002F Encoding.Unicode = UTF-16LE\n\n\u002F\u002F 验证：\n\u002F\u002F 'H' = 0x48, 0x00\n\u002F\u002F 'e' = 0x65, 0x00\n\u002F\u002F '世' = 0x16, 0x4E\n```\n\n```cpp\n\u002F\u002F C++ 端：wchar_t* 就是 UTF-16LE\nconst wchar_t* wstr = reinterpret_cast\u003Cconst wchar_t*>(pCds->lpData);\n\u002F\u002F 直接可用，无需转换\n```\n\n如果 C++ 端需要 `std::string`（UTF-8 或 ANSI），使用 `WideCharToMultiByte` 转换：\n\n```cpp\nstd::string WideToUtf8(const wchar_t* wstr) {\n    int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);\n    std::string result(size - 1, 0);\n    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &result[0], size, nullptr, nullptr);\n    return result;\n}\n```\n\n## 错误处理\n\n```csharp\npublic bool SendStringSafe(IntPtr hwndTarget, IntPtr hwndSelf, string message)\n{\n    \u002F\u002F 1. 验证目标窗口\n    if (!Win32Helper.IsWindow(hwndTarget))\n    {\n        Console.Error.WriteLine(\"Target window is invalid\");\n        return false;\n    }\n    \n    \u002F\u002F 2. 验证输入\n    if (string.IsNullOrEmpty(message))\n        return false;\n    \n    try\n    {\n        byte[] bytes = Encoding.Unicode.GetBytes(message + \"\\0\");\n        IntPtr pData = Marshal.AllocHGlobal(bytes.Length);\n        \n        try\n        {\n            Marshal.Copy(bytes, 0, pData, bytes.Length);\n            \n            COPYDATASTRUCT cds = new COPYDATASTRUCT\n            {\n                dwData = new IntPtr(1),\n                cbData = bytes.Length,\n                lpData = pData\n            };\n            \n            \u002F\u002F SendMessage 会等待 C++ 处理完\n            IntPtr result = SendMessageCopyData(hwndTarget, WM_COPYDATA, hwndSelf, ref cds);\n            \n            if (result == IntPtr.Zero)\n            {\n                int error = Marshal.GetLastWin32Error();\n                Console.Error.WriteLine($\"SendMessage failed, Win32 error: {error}\");\n                return false;\n            }\n            \n            return true;\n        }\n        finally\n        {\n            Marshal.FreeHGlobal(pData);\n        }\n    }\n    catch (Exception ex)\n    {\n        Console.Error.WriteLine($\"Exception: {ex.Message}\");\n        return false;\n    }\n}\n```\n\n## 完整示例：C# 控制 C++ 播放器\n\n```csharp\n\u002F\u002F C# 控制端\npublic class PlayerController : IDisposable\n{\n    private IntPtr _hwndPlayer = IntPtr.Zero;\n    private IntPtr _hwndSelf;\n    \n    public const uint WM_APP_OPEN_FILE = 0x8001;\n    public const uint WM_APP_PLAY     = 0x8002;\n    public const uint WM_APP_PAUSE    = 0x8003;\n    public const uint WM_APP_STOP     = 0x8004;\n    \n    public bool Connect(string windowTitle)\n    {\n        _hwndPlayer = FindWindow(null, windowTitle);\n        return _hwndPlayer != IntPtr.Zero;\n    }\n    \n    public bool OpenFile(string filePath)\n    {\n        if (_hwndPlayer == IntPtr.Zero) return false;\n        return SendFilePath(_hwndPlayer, _hwndSelf, filePath);\n    }\n    \n    public void Play()  => PostMessage(_hwndPlayer, WM_APP_PLAY, IntPtr.Zero, IntPtr.Zero);\n    public void Pause() => PostMessage(_hwndPlayer, WM_APP_PAUSE, IntPtr.Zero, IntPtr.Zero);\n    public void Stop()  => PostMessage(_hwndPlayer, WM_APP_STOP, IntPtr.Zero, IntPtr.Zero);\n    \n    \u002F\u002F ... SendFilePath 实现使用 WM_COPYDATA\n    \n    public void Dispose()\n    {\n        _hwndPlayer = IntPtr.Zero;\n    }\n}\n```\n\n通过以上方案，C# 程序可以安全、可靠地与 C++ 程序进行消息通信，无需复杂的中间件或协议。\n","\u003Ch1>C# 通过 SendMessage 向 C++ 窗口发送消息与字符串\u003C\u002Fh1>\n\u003Cp>在实际项目中，经常需要让 C# 程序与已有的 C++ 程序进行通信。基于 Windows 消息机制的进程间通信（IPC）是一种轻量、无需额外框架的方案。本文详细介绍如何用 C# 向 C++ 窗口发送消息和字符串数据。\u003C\u002Fp>\n\u003Ch2 id=\"跨语言-ipc-背景\">跨语言 IPC 背景\u003C\u002Fh2>\n\u003Cp>Windows 进程间通信有多种方式：\u003C\u002Fp>\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>Windows 消息（WM_COPYDATA）\u003C\u002Ftd>\n\u003Ctd>简单，无额外依赖\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>最快\u003C\u002Ftd>\n\u003Ctd>需要同步机制\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>COM\u002FDCOM\u003C\u002Ftd>\n\u003Ctd>标准化，功能强\u003C\u002Ftd>\n\u003Ctd>注册复杂，重量级\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>套接字（localhost）\u003C\u002Ftd>\n\u003Ctd>跨语言通用\u003C\u002Ftd>\n\u003Ctd>有网络开销\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>文件\u002F注册表\u003C\u002Ftd>\n\u003Ctd>简单\u003C\u002Ftd>\n\u003Ctd>慢，需要轮询\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>对于简单的&quot;C# 控制 C++ 程序&quot;场景，\u003Ccode>FindWindow\u003C\u002Fcode> + \u003Ccode>WM_COPYDATA\u003C\u002Fcode>（或自定义消息）是最快的实现路径。\u003C\u002Fp>\n\u003Ch2 id=\"findwindow-findwindowex-定位目标窗口\">FindWindow\u002FFindWindowEx 定位目标窗口\u003C\u002Fh2>\n\u003Cp>在发送消息前，需要获取目标窗口句柄：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-csharp\">using System;\nusing System.Runtime.InteropServices;\n\npublic class Win32Helper\n{\n    \u002F\u002F P\u002FInvoke 声明\n    [DllImport(&quot;user32.dll&quot;, SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);\n    \n    [DllImport(&quot;user32.dll&quot;, SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern IntPtr FindWindowEx(\n        IntPtr hwndParent, \n        IntPtr hwndChildAfter, \n        string lpszClass, \n        string lpszWindow);\n    \n    [DllImport(&quot;user32.dll&quot;)]\n    public static extern bool IsWindow(IntPtr hWnd);\n}\n\n\u002F\u002F 使用\npublic class MessageSender\n{\n    public IntPtr FindTargetWindow()\n    {\n        \u002F\u002F 按窗口标题查找\n        IntPtr hwnd = Win32Helper.FindWindow(null, &quot;My C++ App&quot;);\n        \n        if (hwnd == IntPtr.Zero)\n        {\n            int error = Marshal.GetLastWin32Error();\n            Console.WriteLine($&quot;FindWindow failed, error: {error}&quot;);\n            return IntPtr.Zero;\n        }\n        \n        \u002F\u002F 验证窗口有效\n        if (!Win32Helper.IsWindow(hwnd))\n        {\n            Console.WriteLine(&quot;Handle is no longer valid&quot;);\n            return IntPtr.Zero;\n        }\n        \n        return hwnd;\n    }\n    \n    public IntPtr FindChildWindow(IntPtr hwndParent, string childClass)\n    {\n        \u002F\u002F 查找子窗口（如特定控件）\n        return Win32Helper.FindWindowEx(hwndParent, IntPtr.Zero, childClass, null);\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"wm_copydata-传递字符串\">WM_COPYDATA 传递字符串\u003C\u002Fh2>\n\u003Cp>\u003Ccode>WM_COPYDATA\u003C\u002Fcode> 是 Windows 专门为跨进程传递数据设计的消息，系统会自动处理内存映射。\u003C\u002Fp>\n\u003Ch3 id=\"c-发送端\">C# 发送端\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-csharp\">using System;\nusing System.Runtime.InteropServices;\nusing System.Text;\n\n[StructLayout(LayoutKind.Sequential)]\npublic struct COPYDATASTRUCT\n{\n    public IntPtr dwData;    \u002F\u002F 用户自定义的数据标识\n    public int cbData;       \u002F\u002F lpData 指向的数据大小（字节）\n    public IntPtr lpData;    \u002F\u002F 指向数据的指针\n}\n\npublic class CopyDataSender\n{\n    [DllImport(&quot;user32.dll&quot;, SetLastError = true)]\n    private static extern IntPtr SendMessage(\n        IntPtr hWnd, \n        uint Msg, \n        IntPtr wParam, \n        ref COPYDATASTRUCT lParam);\n    \n    private const uint WM_COPYDATA = 0x004A;\n    private const int DATA_TYPE_STRING = 1;  \u002F\u002F 自定义的数据类型标识\n    \n    public bool SendString(IntPtr hwndTarget, IntPtr hwndSelf, string message)\n    {\n        if (hwndTarget == IntPtr.Zero || string.IsNullOrEmpty(message))\n            return false;\n        \n        \u002F\u002F 将 C# string 转为 UTF-16 字节（与 C++ wchar_t* 兼容）\n        byte[] bytes = Encoding.Unicode.GetBytes(message + &quot;\\0&quot;);  \u002F\u002F 包含 null 终止符\n        \n        \u002F\u002F 在非托管内存中分配，避免 GC 移动\n        IntPtr pData = Marshal.AllocHGlobal(bytes.Length);\n        try\n        {\n            Marshal.Copy(bytes, 0, pData, bytes.Length);\n            \n            COPYDATASTRUCT cds = new COPYDATASTRUCT\n            {\n                dwData = new IntPtr(DATA_TYPE_STRING),\n                cbData = bytes.Length,\n                lpData = pData\n            };\n            \n            IntPtr result = SendMessage(hwndTarget, WM_COPYDATA, hwndSelf, ref cds);\n            return result != IntPtr.Zero;\n        }\n        finally\n        {\n            Marshal.FreeHGlobal(pData);  \u002F\u002F 必须释放\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"c-接收端\">C++ 接收端\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#define WM_COPYDATA 0x004A\n\nLRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\n{\n    switch (msg)\n    {\n    case WM_COPYDATA:\n    {\n        COPYDATASTRUCT* pCds = reinterpret_cast&lt;COPYDATASTRUCT*&gt;(lParam);\n        \n        if (pCds-&gt;dwData == 1)  \u002F\u002F DATA_TYPE_STRING\n        {\n            \u002F\u002F lpData 是 UTF-16 字符串（C# 的 Unicode 编码）\n            const wchar_t* wstr = reinterpret_cast&lt;const wchar_t*&gt;(pCds-&gt;lpData);\n            int charCount = pCds-&gt;cbData \u002F sizeof(wchar_t) - 1;  \u002F\u002F 减去 null 终止符\n            \n            std::wstring received(wstr, charCount);\n            \n            \u002F\u002F 使用接收到的字符串\n            MessageBoxW(hwnd, received.c_str(), L&quot;Received&quot;, MB_OK);\n        }\n        \n        return TRUE;  \u002F\u002F 告诉发送方消息已处理\n    }\n    \u002F\u002F ...\n    }\n    return DefWindowProc(hwnd, msg, wParam, lParam);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"自定义-wm_app-消息传递简单数值\">自定义 WM_APP 消息传递简单数值\u003C\u002Fh2>\n\u003Cp>对于简单的控制命令（不需要传字符串），使用 \u003Ccode>WM_APP\u003C\u002Fcode> 范围的自定义消息更简单：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-csharp\">\u002F\u002F C# 发送端\n[DllImport(&quot;user32.dll&quot;, SetLastError = true)]\nprivate static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);\n\n[DllImport(&quot;user32.dll&quot;, SetLastError = true)]\nprivate static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);\n\n\u002F\u002F 自定义消息定义（需与 C++ 端一致）\nprivate const uint WM_APP_CMD_PLAY  = 0x8000 + 1;  \u002F\u002F WM_APP = 0x8000\nprivate const uint WM_APP_CMD_PAUSE = 0x8000 + 2;\nprivate const uint WM_APP_CMD_SEEK  = 0x8000 + 3;\n\npublic void SendCommand(IntPtr hwndTarget, int command, int param = 0)\n{\n    PostMessage(hwndTarget, WM_APP_CMD_PLAY + (uint)command, \n                new IntPtr(param), IntPtr.Zero);\n}\n\n\u002F\u002F 求返回值时用 SendMessage\npublic int QueryStatus(IntPtr hwndTarget)\n{\n    IntPtr result = SendMessage(hwndTarget, WM_APP_CMD_SEEK, IntPtr.Zero, IntPtr.Zero);\n    return result.ToInt32();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F C++ 接收端\n#define WM_APP_CMD_PLAY  (WM_APP + 1)\n#define WM_APP_CMD_PAUSE (WM_APP + 2)\n#define WM_APP_CMD_SEEK  (WM_APP + 3)\n\ncase WM_APP_CMD_PLAY:\n    StartPlayback();\n    return 1;\n\ncase WM_APP_CMD_PAUSE:\n    PausePlayback();\n    return 1;\n\ncase WM_APP_CMD_SEEK:\n    return GetCurrentPosition();  \u002F\u002F 返回给 SendMessage 调用方\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"p-invoke-声明完整参考\">P\u002FInvoke 声明完整参考\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-csharp\">using System;\nusing System.Runtime.InteropServices;\nusing System.Text;\n\npublic static class User32\n{\n    \u002F\u002F 基本消息发送\n    [DllImport(&quot;user32.dll&quot;, SetLastError = true)]\n    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, \n                                             IntPtr wParam, IntPtr lParam);\n    \n    [DllImport(&quot;user32.dll&quot;, SetLastError = true)]\n    public static extern bool PostMessage(IntPtr hWnd, uint Msg, \n                                          IntPtr wParam, IntPtr lParam);\n    \n    \u002F\u002F WM_COPYDATA 专用重载\n    [DllImport(&quot;user32.dll&quot;, SetLastError = true)]\n    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, \n                                             IntPtr wParam, ref COPYDATASTRUCT lParam);\n    \n    \u002F\u002F 字符串版本（用于 WM_GETTEXT 等）\n    [DllImport(&quot;user32.dll&quot;, SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern int SendMessage(IntPtr hWnd, uint Msg, \n                                          int wParam, StringBuilder lParam);\n    \n    \u002F\u002F 窗口查找\n    [DllImport(&quot;user32.dll&quot;, SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);\n    \n    [DllImport(&quot;user32.dll&quot;, SetLastError = true, CharSet = CharSet.Unicode)]\n    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,\n                                              string lpszClass, string lpszWindow);\n    \n    \u002F\u002F 消息常量\n    public const uint WM_COPYDATA  = 0x004A;\n    public const uint WM_SETTEXT   = 0x000C;\n    public const uint WM_GETTEXT   = 0x000D;\n    public const uint WM_APP       = 0x8000;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"字符编码对齐-utf-16\">字符编码对齐（UTF-16）\u003C\u002Fh2>\n\u003Cp>C# 的 \u003Ccode>string\u003C\u002Fcode> 和 C++ 的 \u003Ccode>std::wstring\u003C\u002Fcode>\u002F\u003Ccode>wchar_t*\u003C\u002Fcode> 都使用 UTF-16LE，天然兼容。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-csharp\">\u002F\u002F C# 端：string → UTF-16 字节\nstring msg = &quot;Hello, 世界&quot;;\nbyte[] utf16Bytes = Encoding.Unicode.GetBytes(msg);  \u002F\u002F Encoding.Unicode = UTF-16LE\n\n\u002F\u002F 验证：\n\u002F\u002F 'H' = 0x48, 0x00\n\u002F\u002F 'e' = 0x65, 0x00\n\u002F\u002F '世' = 0x16, 0x4E\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-cpp\">\u002F\u002F C++ 端：wchar_t* 就是 UTF-16LE\nconst wchar_t* wstr = reinterpret_cast&lt;const wchar_t*&gt;(pCds-&gt;lpData);\n\u002F\u002F 直接可用，无需转换\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>如果 C++ 端需要 \u003Ccode>std::string\u003C\u002Fcode>（UTF-8 或 ANSI），使用 \u003Ccode>WideCharToMultiByte\u003C\u002Fcode> 转换：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">std::string WideToUtf8(const wchar_t* wstr) {\n    int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);\n    std::string result(size - 1, 0);\n    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &amp;result[0], size, nullptr, nullptr);\n    return result;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"错误处理\">错误处理\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-csharp\">public bool SendStringSafe(IntPtr hwndTarget, IntPtr hwndSelf, string message)\n{\n    \u002F\u002F 1. 验证目标窗口\n    if (!Win32Helper.IsWindow(hwndTarget))\n    {\n        Console.Error.WriteLine(&quot;Target window is invalid&quot;);\n        return false;\n    }\n    \n    \u002F\u002F 2. 验证输入\n    if (string.IsNullOrEmpty(message))\n        return false;\n    \n    try\n    {\n        byte[] bytes = Encoding.Unicode.GetBytes(message + &quot;\\0&quot;);\n        IntPtr pData = Marshal.AllocHGlobal(bytes.Length);\n        \n        try\n        {\n            Marshal.Copy(bytes, 0, pData, bytes.Length);\n            \n            COPYDATASTRUCT cds = new COPYDATASTRUCT\n            {\n                dwData = new IntPtr(1),\n                cbData = bytes.Length,\n                lpData = pData\n            };\n            \n            \u002F\u002F SendMessage 会等待 C++ 处理完\n            IntPtr result = SendMessageCopyData(hwndTarget, WM_COPYDATA, hwndSelf, ref cds);\n            \n            if (result == IntPtr.Zero)\n            {\n                int error = Marshal.GetLastWin32Error();\n                Console.Error.WriteLine($&quot;SendMessage failed, Win32 error: {error}&quot;);\n                return false;\n            }\n            \n            return true;\n        }\n        finally\n        {\n            Marshal.FreeHGlobal(pData);\n        }\n    }\n    catch (Exception ex)\n    {\n        Console.Error.WriteLine($&quot;Exception: {ex.Message}&quot;);\n        return false;\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"完整示例-c-控制-c-播放器\">完整示例：C# 控制 C++ 播放器\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-csharp\">\u002F\u002F C# 控制端\npublic class PlayerController : IDisposable\n{\n    private IntPtr _hwndPlayer = IntPtr.Zero;\n    private IntPtr _hwndSelf;\n    \n    public const uint WM_APP_OPEN_FILE = 0x8001;\n    public const uint WM_APP_PLAY     = 0x8002;\n    public const uint WM_APP_PAUSE    = 0x8003;\n    public const uint WM_APP_STOP     = 0x8004;\n    \n    public bool Connect(string windowTitle)\n    {\n        _hwndPlayer = FindWindow(null, windowTitle);\n        return _hwndPlayer != IntPtr.Zero;\n    }\n    \n    public bool OpenFile(string filePath)\n    {\n        if (_hwndPlayer == IntPtr.Zero) return false;\n        return SendFilePath(_hwndPlayer, _hwndSelf, filePath);\n    }\n    \n    public void Play()  =&gt; PostMessage(_hwndPlayer, WM_APP_PLAY, IntPtr.Zero, IntPtr.Zero);\n    public void Pause() =&gt; PostMessage(_hwndPlayer, WM_APP_PAUSE, IntPtr.Zero, IntPtr.Zero);\n    public void Stop()  =&gt; PostMessage(_hwndPlayer, WM_APP_STOP, IntPtr.Zero, IntPtr.Zero);\n    \n    \u002F\u002F ... SendFilePath 实现使用 WM_COPYDATA\n    \n    public void Dispose()\n    {\n        _hwndPlayer = IntPtr.Zero;\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>通过以上方案，C# 程序可以安全、可靠地与 C++ 程序进行消息通信，无需复杂的中间件或协议。\u003C\u002Fp>\n","2023-06-09",[11,12,13,14,15],"C#","cpp","windows","互操作","PInvoke",false,[18,31,42,54,64,71,78,85,92,99,109,118,128,137,145,153,162,171,180,190,197,206,212,219,225,234,241,248,256,266,275,284,294,304,314,322,332,343,351,354,362,368,376,384,392,400,408,415],{"slug":19,"title":20,"description":21,"pub_date":22,"tags":23,"draft":16,"word_count":30},"ide-skills-guide","Agent Skills 完全指南：21 款第三方 Skill 深度评测与使用心得","全面评测 21 款第三方 Agent Skills，涵盖 Vue 生态、前端设计、构建工具、实用工具四大分类。从安装配置到实际使用场景，带你了解每个 Skill 的功能特点、最佳实践与使用心得。","2026-06-15",[24,25,26,27,28,29],"agent","skills","AI","效率工具","前端","Vue",4169,{"slug":32,"title":33,"description":34,"pub_date":35,"tags":36,"draft":16,"word_count":41},"linux-kernel-skeleton-struct-funcptr-container_of","Linux 内核骨架：struct、函数指针与 container_of","读懂 Linux 内核源码的三件套：巨大的 struct 组合代替继承、函数指针表实现虚派发、container_of 宏从嵌入成员找回完整对象。","2026-05-09",[37,38,39,40],"linux","kernel","C","container_of",1369,{"slug":43,"title":44,"description":45,"pub_date":46,"tags":47,"draft":16,"word_count":53},"astro-complete-guide-2025","Astro 5 深度剖析：Islands 架构原理、构建优化与 Cloudflare Workers 边缘部署","从编译器视角解析 Astro 5 的 Islands 架构实现原理，Content Layer API 的 Vite 插件机制，Server Islands 的流式渲染，以及如何在 Cloudflare Workers + D1 边缘环境下榨干性能。","2026-05-08",[48,49,50,51,52],"astro","frontend","cloudflare","performance","architecture",3663,{"slug":55,"title":56,"description":57,"pub_date":58,"tags":59,"draft":16,"word_count":63},"llm-prompt-engineering","Prompt Engineering 实战：让 LLM 真正听话的技巧","System prompt 怎么写、Few-shot 怎么设计、Chain-of-Thought 原理，以及常见失败模式和调试方法。","2026-05-03",[60,61,62],"ai","llm","工程实践",1723,{"slug":65,"title":66,"description":67,"pub_date":58,"tags":68,"draft":16,"word_count":70},"rag-system-design","RAG 系统设计：从 naive 到 production-ready","Retrieval-Augmented Generation 不只是「向量数据库 + LLM」，分块策略、召回质量、重排序、缓存才是工程核心。",[60,69,61,62],"rag",1613,{"slug":72,"title":73,"description":74,"pub_date":58,"tags":75,"draft":16,"word_count":77},"git-advanced-workflow","Git 进阶工作流：rebase、cherry-pick、bisect 的正确使用","merge 会了，但 rebase 总搞错？bisect 找 bug 提交？interactive rebase 整理历史？这篇一次说清楚。",[76,62],"git",1396,{"slug":79,"title":80,"description":81,"pub_date":58,"tags":82,"draft":16,"word_count":84},"docker-practical-guide","Docker 实战：从会用到用好","会 docker run 不够，Dockerfile 最佳实践、多阶段构建、Compose 编排、镜像瘦身才是日常真正需要的。",[83,37,62],"docker",1268,{"slug":86,"title":87,"description":88,"pub_date":58,"tags":89,"draft":16,"word_count":91},"anthropics-skills-guide","anthropics\u002Fskills：Anthropic 官方 Agent Skills 仓库解析","Anthropic 官方开源的 Agent Skills 标准仓库，127k stars，解析 SKILL.md 规范、17 个示例 skill 的设计模式，以及如何在 Claude Code \u002F Claude.ai \u002F API 中使用",[60,90,24,25],"Claude",2090,{"slug":93,"title":94,"description":95,"pub_date":58,"tags":96,"draft":16,"word_count":98},"karpathy-claude-code-guidelines","Karpathy 的 LLM 编码批评与 CLAUDE.md 最佳实践","基于 Andrej Karpathy 对 LLM 编程助手的观察，forrestchang 提炼出一个 CLAUDE.md 文件，4 条原则解决 AI 编码的典型失控问题：乱猜假设、过度设计、乱改代码、目标不清",[60,90,97,62],"Claude Code",2699,{"slug":100,"title":101,"description":102,"pub_date":58,"tags":103,"draft":16,"word_count":108},"typescript-advanced-patterns","TypeScript 高级模式：让类型系统为你工作","基础 TS 会了但类型总是 any？条件类型、映射类型、模板字面量类型、infer 关键字才是 TS 的真正威力。",[104,105,106,107],"typescript","类型系统","前端工程","高级模式",1419,{"slug":110,"title":111,"description":112,"pub_date":58,"tags":113,"draft":16,"word_count":117},"linux-performance-tuning","Linux 性能调优实战：从 top 到 perf 的完整工具链","遇到性能问题不知道从哪下手？这篇建立系统化的排查思路，从 CPU\u002F内存\u002FIO\u002F网络逐层分析。",[37,114,115,116],"性能","运维","系统编程",1524,{"slug":119,"title":120,"description":121,"pub_date":58,"tags":122,"draft":16,"word_count":127},"python-functional-programming","Python 函数式编程：map\u002Ffilter\u002Freduce 之外","Python 不是纯函数式语言，但 functools、itertools、偏函数、闭包这些工具用好了能让代码简洁一个量级。",[123,124,125,126],"python","函数式","闭包","装饰器",1867,{"slug":129,"title":130,"description":131,"pub_date":58,"tags":132,"draft":16,"word_count":136},"python-oop-guide","Python 面向对象：__init__ 之外你需要知道的","Python OOP 不只是 class + __init__，魔术方法、描述符、元类才是真正的武器。",[123,133,134,135],"OOP","面向对象","魔术方法",1792,{"slug":138,"title":139,"description":140,"pub_date":58,"tags":141,"draft":16,"word_count":144},"python-data-structures","Python 内置数据结构深度解析","list、dict、set、tuple 不只是数据容器，搞懂它们的底层实现和时间复杂度，才能写出高性能 Python。",[123,142,114,143],"数据结构","算法",1517,{"slug":146,"title":147,"description":148,"pub_date":58,"tags":149,"draft":16,"word_count":152},"python-basics-quick-start","Python 快速上手：写给有编程基础的人","已经会其他语言，想快速掌握 Python 的语法特性和思维方式，这篇是捷径。",[123,150,151],"入门","基础",1607,{"slug":154,"title":155,"description":156,"pub_date":58,"tags":157,"draft":16,"word_count":161},"python-dataclass-pydantic","Python dataclass vs Pydantic：数据类选型指南","dataclass 是标准库的轻量选择，Pydantic v2 是带验证的重武器，什么时候用哪个，这篇说清楚。",[123,158,159,160],"dataclass","pydantic","数据验证",1323,{"slug":163,"title":164,"description":165,"pub_date":58,"tags":166,"draft":16,"word_count":170},"python-asyncio-practical","Python asyncio 实战：从回调地狱到协程优雅","asyncio 是 Python 异步编程的核心，搞懂 event loop、Task、gather 这些概念才能写出真正高效的异步代码。",[123,167,168,169],"asyncio","并发","网络编程",1258,{"slug":172,"title":173,"description":174,"pub_date":58,"tags":175,"draft":16,"word_count":179},"python-type-hints-guide","Python 类型注解完全指南：从入门到实践","Python 3.5+ 引入类型注解，配合 mypy\u002Fpyright 让 Python 也能享受静态类型检查的好处。",[123,176,177,178],"typescript-style","type-hints","工具链",1102,{"slug":181,"title":182,"description":183,"pub_date":184,"tags":185,"draft":16,"word_count":189},"pwa-install-update-button","PWA 踩坑：为什么安装按钮从来不出现","从 beforeinstallprompt 到 Service Worker waiting，把 PWA 的安装与更新提示真正做对","2026-05-02",[186,187,188],"pwa","javascript","web",1683,{"slug":191,"title":192,"description":193,"pub_date":194,"tags":195,"draft":16,"word_count":196},"openclaw-vs-hermes-agent","OpenClaw vs Hermes Agent：两个本地优先 Agent 的设计差异","OpenClaw（Novita AI）和 Hermes Agent（Nous Research）都是本地运行的个人 AI Agent，但在记忆系统、技能学习、运行环境和模型生态上走了不同的路。深入对比两种架构的核心差异。","2026-05-01",[60,24,61],1679,{"slug":198,"title":199,"description":200,"pub_date":194,"tags":201,"draft":16,"word_count":205},"cpp-random-design-patterns","C++ 设计模式实战：RAII、观察者、工厂","用现代 C++（C++17\u002F20）实现三种高频设计模式：RAII 资源管理、观察者模式事件系统、工厂模式插件架构。每种模式给出问题场景、实现代码和真实工程案例。",[12,202,203,204],"设计模式","c++17","工程",2613,{"slug":207,"title":208,"description":209,"pub_date":194,"tags":210,"draft":16,"word_count":211},"data-structures-fundamentals","数据结构基础：从数组到红黑树","系统梳理常用数据结构的核心原理、时间复杂度和适用场景。数组、链表、栈、队列、哈希表、二叉树、堆、图，每种结构附实现要点和 C++ 代码片段。",[142,143,12,151],3004,{"slug":213,"title":214,"description":215,"pub_date":216,"tags":217,"draft":16,"word_count":218},"ai-agent-what-is","什么是 AI Agent？从 LLM 到自主执行","LLM 本身是无状态问答机，Agent 是什么让它’动’起来的？本文深入解析 Agent 的四个核心能力、ReAct 框架、工具调用原理，以及主流框架横向对比。","2026-04-30",[60,24,61],2116,{"slug":220,"title":221,"description":222,"pub_date":216,"tags":223,"draft":16,"word_count":224},"ai-agent-memory","AI Agent 的记忆系统：从上下文窗口到长期记忆","深入拆解 AI Agent 的四种记忆类型、上下文窗口压缩策略、RAG 向量检索原理，以及三种典型失败模式和工程选型建议。",[60,24,69],2052,{"slug":226,"title":227,"description":228,"pub_date":216,"tags":229,"draft":16,"word_count":233},"network-proxy-vpn-guide","代理与翻墙技术原理：从 HTTP 代理到现代协议","深入解析代理与 VPN 的本质区别，梳理从 SOCKS5 到 Shadowsocks、V2Ray\u002FXray、Hysteria2 的协议演进，以及机场订阅的技术本质。",[230,231,232],"网络","代理","协议",2148,{"slug":235,"title":236,"description":237,"pub_date":216,"tags":238,"draft":16,"word_count":152},"algorithm-binary-search","二分查找：永远写不对？记住这个模板","彻底搞清楚二分查找的边界问题：闭区间和左闭右开两套模板、三道经典 LeetCode 题目完整 C++ 实现，以及二分答案的进阶思路。",[143,239,240,12],"二分查找","leetcode",{"slug":242,"title":243,"description":244,"pub_date":216,"tags":245,"draft":16,"word_count":247},"algorithm-sliding-window","滑动窗口算法：从暴力到 O(n) 的思维跃迁","系统讲解滑动窗口算法的核心模板、适用题型，配合三道经典 LeetCode 题目的完整 C++ 实现，彻底理解双指针收缩思路。",[143,246,240,12],"滑动窗口",1943,{"slug":249,"title":250,"description":251,"pub_date":216,"tags":252,"draft":16,"word_count":255},"network-clash-config","Clash \u002F Mihomo 配置详解：规则、策略组与分流","深入解析 Clash\u002FMihomo 的核心配置结构，包括代理节点、策略组类型、规则优先级、DNS fake-ip 模式，以及一份实用的完整配置模板。",[230,253,231,254],"clash","配置",1292,{"slug":257,"title":258,"description":259,"pub_date":260,"tags":261,"draft":16,"word_count":265},"hid-hotplug","HID 设备热插拔检测：从 udev 到 node-hid","在 Linux 上用 node-hid + usb 库实现可靠的 USB HID 设备热插拔检测，踩坑记录","2026-04-28",[12,262,37,263,264],"hid","nodejs","electron",2039,{"slug":267,"title":268,"description":269,"pub_date":270,"tags":271,"draft":16,"word_count":274},"electron-ipc-types","Electron IPC 类型安全：从 any 到完全类型化","用 TypeScript 泛型封装 Electron IPC，彻底消灭 any，preload 契约集中管理","2026-04-25",[264,104,272,273],"ipc","vue",1446,{"slug":276,"title":277,"description":278,"pub_date":279,"tags":280,"draft":16,"word_count":283},"element-plus-popover-hide","手动关闭多个 el-popover（不用 v-model:visible）","通过 ref + Reflect.get 调用 hide() 方法手动关闭 Element Plus Popover，解释 Vue3 Proxy 导致无法直接调用实例方法的原因。","2024-10-25",[273,281,282],"element-plus","vue3",1321,{"slug":285,"title":286,"description":287,"pub_date":288,"tags":289,"draft":16,"word_count":293},"vite-vue3-ts-elementplus-pinia","用 Vite+（vp）从零搭建 Vue3 + TypeScript + Element Plus + Pinia + Vue Router","使用 Vite+ 统一工具链（vp）一条命令搭建 Vue3 全家桶，涵盖按需导入、Pinia store、路由配置，以及常见坑的解决方案。","2024-08-27",[273,290,104,281,291,292],"vite","pinia","vite-plus",1960,{"slug":295,"title":296,"description":297,"pub_date":298,"tags":299,"draft":16,"word_count":303},"cef-lnk2038-iterator-debug-level","CEF LNK2038：解决 _ITERATOR_DEBUG_LEVEL 不匹配错误","分析 CEF（Chromium Embedded Framework）集成时出现的 LNK2038 _ITERATOR_DEBUG_LEVEL 链接错误，从根本原因到解决方案的完整指南。","2024-05-07",[12,300,301,302],"CEF","Visual Studio","链接错误",1509,{"slug":305,"title":306,"description":307,"pub_date":308,"tags":309,"draft":16,"word_count":313},"npm-electron-install-fix","彻底解决 npm 安装 Electron 失败的问题","分析 npm install electron 失败的根本原因（下载二进制超时\u002F被墙），通过国内镜像（npmmirror）彻底解决，并介绍多种备选方案和常见错误排查。","2024-03-01",[264,310,311,312],"npm","前端工具链","国内镜像",1494,{"slug":315,"title":316,"description":317,"pub_date":318,"tags":319,"draft":16,"word_count":321},"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",[76,37,320],"工具",2244,{"slug":323,"title":324,"description":325,"pub_date":326,"tags":327,"draft":16,"word_count":331},"vmware-tools-install","在 VMware 虚拟机中安装 open-vm-tools 完整指南","详解 VMware Tools 的作用、open-vm-tools 与官方 VMware Tools 的区别，以及在 Ubuntu 虚拟机中安装并生效的完整步骤和常见问题排查。","2023-11-21",[328,37,329,330],"VMware","Ubuntu","虚拟机",2523,{"slug":333,"title":334,"description":335,"pub_date":336,"tags":337,"draft":16,"word_count":342},"load-balancing-algorithms","负载均衡算法完全指南：从轮询到一致性哈希","系统梳理静态与动态负载均衡算法，涵盖轮询、随机、权重、IP Hash、一致性 Hash、最少连接、最快响应等，并对比 Nginx、Dubbo、Spring Cloud LoadBalancer 的实现差异。","2023-11-15",[338,339,340,341],"分布式","负载均衡","Nginx","微服务",1764,{"slug":344,"title":345,"description":346,"pub_date":9,"tags":347,"draft":16,"word_count":350},"win-cw2a-ca2w","ATL 字符串转换：CW2A 与 CA2W 完全指南","详解 ATL 宏 CW2A\u002FCA2W 在 Unicode 与 ANSI 之间的字符串转换用法、头文件依赖、USES_CONVERSION 宏的作用与常见陷阱。",[12,13,348,349],"ATL","字符串",1665,{"slug":4,"title":5,"description":6,"pub_date":9,"tags":352,"draft":16,"word_count":353},[11,12,13,14,15],1554,{"slug":355,"title":356,"description":357,"pub_date":358,"tags":359,"draft":16,"word_count":361},"win-postmessage-vector","Windows PostMessage 跨线程传递 std::vector 指针","通过 PostMessage 在 Windows 消息队列中传递 std::vector 指针，使用 reinterpret_cast 将指针装入 LPARAM，并在接收方正确释放内存。","2023-05-26",[12,13,360],"WinAPI",1823,{"slug":363,"title":364,"description":365,"pub_date":358,"tags":366,"draft":16,"word_count":367},"exe-dll-single-package","将 EXE 和 DLL 打包成单一可执行文件","介绍两种将 exe 和依赖 dll 打包成单文件的方案：Enigma Virtual Box 和 WinRAR 自解压，适合发布 Windows 桌面程序时简化分发流程。",[13,12,320],1619,{"slug":369,"title":370,"description":371,"pub_date":358,"tags":372,"draft":16,"word_count":375},"cpp-random-mt19937","C++ 现代随机数生成：用 mt19937 彻底告别 rand()","深入讲解为什么 rand() 不够用，以及如何用 C++11 的 \u003Crandom> 库正确生成高质量随机数，涵盖 mt19937、各种分布和线程安全。",[12,373,374],"c++11","random",1549,{"slug":377,"title":378,"description":379,"pub_date":380,"tags":381,"draft":16,"word_count":383},"win-startup-registry","C++ 实现程序开机自启动：注册表方式详解","通过操作 Windows 注册表 Run 键实现程序开机自启动，包括 HKCU 与 HKLM 区别、完整封装代码、工作目录问题和 UAC 权限处理。","2022-12-26",[13,12,382],"registry",1201,{"slug":385,"title":386,"description":387,"pub_date":388,"tags":389,"draft":16,"word_count":391},"mfc-cstring-wparam","MFC 中 CString 与 WPARAM 之间的转换","详解 MFC 消息传递中 CString 无法直接强转为 WPARAM 的原因，以及两种正确的转换方案，并介绍结构体指针传递的正确姿势。","2022-11-25",[390,12,13],"mfc",1546,{"slug":393,"title":394,"description":395,"pub_date":396,"tags":397,"draft":16,"word_count":399},"duilib-static-build","正确编译 Duilib 静态库：避免 ATL 依赖和链接错误","详解如何用 DuiLib_Static.vcxproj 编译 Duilib 静态库，解决 VARIANT 未定义、Unicode 配置不匹配和 ATL 依赖等常见问题。","2022-08-24",[12,398,13,390],"duilib",2639,{"slug":401,"title":402,"description":403,"pub_date":404,"tags":405,"draft":16,"word_count":407},"mfc-dpi-adaptive","MFC 界面自适应不同分辨率","MFC 对话框程序实现控件和字体随分辨率自动缩放的完整方案，附 DPI Awareness 配置说明","2022-08-17",[390,12,13,406],"dpi",1414,{"slug":409,"title":410,"description":411,"pub_date":412,"tags":413,"draft":16,"word_count":414},"mfc-drag-window","MFC 无标题栏窗口客户区拖动：三种方法对比","MFC 对话框去掉标题栏后如何实现拖动移动窗口，三种方案完整实现与适用场景分析","2022-08-16",[390,12,13],1633,{"slug":416,"title":417,"description":418,"pub_date":419,"tags":420,"draft":16,"word_count":422},"algorithm-number-complement","整数的补数：位运算掩码解法","LeetCode 476 题，用掩码 XOR 实现整数补数，附 C++\u002FPython\u002FJava 三种实现及补数与补码的区别","2021-03-08",[143,421,240],"位运算",1374,[]]