简介:这份资源面向在 Windows 平台使用 VS2019 进行 C++ 开发的工程师,提供已静态编译完成的 libjsoncpp 与 libjson-rpc-cpp 库,解决部分公开版本缺少 libjsoncpp 文件、仅提供 x86 架构而无法直接使用的问题。资源为 X64 版本,附带 sln 工程文件与编译好的 lib 库,可直接集成到项目中,也支持按需二次编译为 X64 或 x86。压缩包共 355 个文件,约 20.82MB,涵盖 cpp、h 源码与头文件,lib、pdb 等编译产物,vcxproj、sln 工程配置,以及 json、expected 测试数据与构建脚本,目录结构完整,便于追溯编译过程与排查依赖问题。目前已有 477 人学习下载,适合需要快速接入 JSON 解析与 JSON-RPC 通信能力、又不想在编译环境上反复踩坑的开发者参考使用。
1. 拿到静态库先别急着往工程里拖:VS2019 下 libjsoncpp 与 libjson-rpc-cpp 的版本对齐
上周帮一个做桌面端配置工具的朋友收拾烂摊子:他从别处拷来一份号称“VS2019 静态编译好”的 libjsoncpp 和 libjson-rpc-cpp,直接拖进自己的 MFC 工程,结果 LNK2038 运行库不匹配、LNK2001 一堆符号找不到,折腾一下午没跑起来。这类预编译静态库最坑的地方不在库本身,而在“编译它的人用的运行库、字符集、平台版本”和你当前工程是否一致。libjsoncpp 负责 JSON 解析与序列化,libjson-rpc-cpp 在其上做 JSON-RPC 客户端与服务端封装,两者版本必须配套,否则头文件里的接口对不上。这套资源适合不想自己拉 CMake 编译、希望拿到 .lib 和头文件就能直接链接的 C++ 开发者,尤其是维护老 VS2019 工程、不方便升级工具链的团队。装不好最典型的后果就是编译能过、链接报错,或者链接过了运行时段错误——所以第一步不是写代码,而是把环境对齐。
2. 环境核对:VS2019 工具集、运行库与平台三件套
2.1 先确认工具集版本和平台目标
预编译静态库对工具集(Platform Toolset)敏感。VS2019 默认是 v142,如果你的工程被改成 v141 或 v143,链接阶段大概率出问题。打开工程属性,确认“配置属性 → 常规 → 平台工具集”为 Visual Studio 2019 (v142)。平台目标也要一致:库是 Win32 还是 x64,工程就得对应。常见做法是建一个空的控制台工程先做链接验证,别一上来就往业务工程里塞。
# 在 VS2019 开发者命令提示符下查看当前工具集版本 cl 2>&1 | findstr /C:"Version" # 输出示例:Microsoft (R) C/C++ Optimizing Compiler Version 19.29.xxxxx for x64cl输出的 19.29 对应 VS2019 16.11 系列,v142 工具集。如果你机器上是 19.28 或更早,问题不大,但库若是用更高小版本编译的,个别 STL 符号可能对不上。这一步的目的是确认“编译器大版本一致”,不是要求逐位相同。
2.2 运行库选项必须逐字匹配
静态库最隐蔽的坑是 C/C++ 运行库(Runtime Library)。库用 /MT(多线程静态)编译,你的工程用 /MD(多线程 DLL),链接器会直接甩 LNK2038:RuntimeLibrary 不匹配。四个取值要分清:/MT、/MTd、/MD、/MDd,Debug 配置带 d,Release 不带。预编译库通常只提供 Release 版本,那就意味着你的 Debug 工程也得临时切到 /MT 或 /MD 才能链接通过,调试时再切回来。
| 工程配置 | 运行库选项 | 适用场景 |
|---|---|---|
| Release | /MT | 不依赖 VC 运行库分发的独立程序 |
| Release | /MD | 依赖 vcruntime140.dll 的常规发布 |
| Debug | /MTd | 静态调试版,体积大 |
| Debug | /MDd | 动态调试版,调试信息完整 |
提示:先问清或自己验证库是用哪种运行库编的。方法是在 VS 里打开 .lib 所在目录,用 dumpbin /directives 看编译指令,比猜靠谱。
2.3 字符集与 C++ 标准
libjsoncpp 的头文件对字符集不敏感,但 libjson-rpc-cpp 的部分接口涉及 std::string 与宽字符转换,工程若开了“使用 Unicode 字符集”而库按多字节编译,个别重载会解析失败。我一般统一设为“使用多字节字符集”做首轮验证,跑通后再按业务需要调整。C++ 标准语言级别建议设为 ISO C++14 或 C++17,别用“默认(ISO C++14)以外的实验特性”,预编译库的 ABI 是按稳定标准出的。
3. 目录落位与工程配置:头文件、库目录、附加依赖项
3.1 建立清晰的第三方目录结构
拿到资源后先别散放。我习惯在解决方案同级建 third_party,下面按库名和平台分:
third_party/ jsoncpp/ include/json/ # json.h 等头文件 lib/x64/Release/ # jsoncpp.lib json-rpc-cpp/ include/jsonrpc/ # 客户端与服务端头文件 lib/x64/Release/ # jsonrpccpp-*.lib这样做的原因是后续工程属性里路径可复用,换 Debug/Release、Win32/x64 只改一段。头文件目录要指到 include 的上一级还是 include 本身,取决于头文件里的 include 写法——libjsoncpp 常见是#include <json/json.h>,所以附加包含目录应指到 jsoncpp/include,而不是 jsoncpp/include/json。
3.2 附加包含目录与附加库目录
在工程属性里配置,不要用绝对路径写死到某台机器。VS 支持宏,用$(SolutionDir)相对定位:
# 配置属性 → C/C++ → 常规 → 附加包含目录 $(SolutionDir)third_party\jsoncpp\include $(SolutionDir)third_party\json-rpc-cpp\include # 配置属性 → 链接器 → 常规 → 附加库目录 $(SolutionDir)third_party\jsoncpp\lib\$(Platform)\$(Configuration) $(SolutionDir)third_party\json-rpc-cpp\lib\$(Platform)\$(Configuration)$(Platform)展开为 Win32 或 x64,$(Configuration)展开为 Debug 或 Release,这样一套属性覆盖四种组合,前提是目录真的按这个规则建好了。如果资源只给了 Release 库,Debug 配置下这个路径会指向不存在的目录,链接器报 LNK1104 找不到 .lib——这时要么补 Debug 库,要么在 Debug 下也引用 Release 库并接受运行库不匹配的风险(不推荐)。
3.3 附加依赖项与链接顺序
链接器 → 输入 → 附加依赖项里填库文件名。libjson-rpc-cpp 依赖 libjsoncpp,顺序上被依赖的放后面:
jsonrpccpp-client.lib jsonrpccpp-server.lib jsonrpccpp-common.lib jsoncpp.lib如果只用到客户端,client 和 common 就够。链接顺序在 MSVC 下不像 GCC 那么严格,但静态库之间存在符号引用时,把提供符号的库放后面能减少 LNK2001。填完先做一次“仅链接”验证:写一个只调用 jsoncpp 解析字符串的空 main,编译链接通过再往下走。
4. 最小验证程序:先让 jsoncpp 解析一段字符串
4.1 写一个不依赖 RPC 的冒烟测试
别一上来就调 RPC 接口,先把 libjsoncpp 跑通,隔离问题。新建控制台工程,代码:
#include <json/json.h> #include <iostream> #include <string> int main() { std::string raw = R"({"name":"demo","version":1,"tags":["a","b"]})"; Json::CharReaderBuilder builder; Json::Value root; std::string errs; std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); bool ok = reader->parse(raw.data(), raw.data() + raw.size(), &root, &errs); if (!ok) { std::cerr << "parse failed: " << errs << std::endl; return 1; } std::cout << root["name"].asString() << " " << root["version"].asInt() << std::endl; return 0; }CharReaderBuilder是 jsoncpp 较新版本的接口,老版本用Json::Reader。如果编译报“CharReaderBuilder 未定义”,说明头文件和库版本偏旧,得换用Json::Reader reader; reader.parse(...)的写法。newCharReader()返回裸指针,用 unique_ptr 包一层避免泄漏。这段代码同时验证了三件事:头文件路径对不对、库链接成不成功、运行库匹配不匹配。
4.2 链接失败的快速定位
编译过、链接报 LNK2019 无法解析的外部符号,先看符号名里有没有__imp_前缀。有前缀说明库是动态导入库(.lib 配套 DLL),而你按静态库在用;没有前缀但报运行库相关,回去查 /MT 与 /MD。LNK2038 直接指向运行库或迭代器调试级别不匹配,后者在 Debug 下混用 Release 库时出现,报_ITERATOR_DEBUG_LEVEL不一致。这类问题没有捷径,只能让库和工程的编译选项对齐。
5. 接入 libjson-rpc-cpp:客户端与服务端的编译取舍
5.1 客户端最小调用
libjson-rpc-cpp 的客户端通常基于 HTTP 或 TCP。静态库版本若已包含传输层实现,直接构造客户端对象即可。以下为结构示意,具体类名以头文件为准:
#include <jsonrpccpp/client/connectors/httpclient.h> #include <jsonrpccpp/client/client.h> int main() { jsonrpc::HttpClient httpClient("http://127.0.0.1:8383"); // 具体 Client 子类由 jsonrpcstub 生成或手写 return 0; }HttpClient构造参数是服务端地址。真正发请求需要继承jsonrpc::Client并声明方法,或用工具根据接口描述生成 stub。静态库只提供运行时,不提供代码生成,这一点新手容易误解——以为链接上库就能直接调任意方法。
5.2 服务端链接的额外依赖
服务端若用 HTTP 传输,静态库可能依赖系统库 ws2_32.lib、winhttp.lib 或第三方 HTTP 实现。链接报 LNK2019 且符号形如__imp_WSAStartup,就在附加依赖项补 ws2_32.lib。这类系统库依赖在预编译包里不会自动带上,得自己补。我一般先把报错符号丢进 dumpbin /symbols 里查在哪个库,再决定加哪个依赖,比盲试快。
5.3 Debug 与 Release 混用的代价
资源若只提供 Release 静态库,Debug 工程强行链接会在运行时出现堆损坏或断言失败,因为 Debug 版 STL 容器布局与 Release 不同。临时办法是 Debug 工程也切 /MD(非 /MDd)并定义 NDEBUG,但断点调试体验会打折。正规做法是让提供方补 Debug 库,或自己用 CMake 按相同选项重编一份。预编译库省的是编译时间,不是省掉版本管理的责任。
6. 避坑排查:五条血泪记录
6.1 LNK2038 RuntimeLibrary 不匹配
现象:链接报LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”。原因:库用 /MT 编译,工程用 /MD。解决:工程属性 → C/C++ → 代码生成 → 运行库,改成与库一致的值;或换用对应运行库版本的库。改完记得四个配置(Debug/Release × Win32/x64)都检查,别只改当前配置。
6.2 LNK2019 找不到 jsoncpp 符号
现象:无法解析的外部符号 "public: __cdecl Json::Value::Value(void)"。原因:附加库目录没指对,或附加依赖项漏了 jsoncpp.lib,或库是 x64 而工程是 Win32。解决:先用 dumpbin /headers jsoncpp.lib 确认机器类型是 x64 还是 x86,再核对工程平台;然后确认附加依赖项拼写。路径里带空格或中文也可能导致链接器静默忽略,尽量放纯英文短路径。
6.3 头文件能找到但接口对不上
现象:编译报CharReaderBuilder 不是 Json 的成员。原因:头文件是旧版 jsoncpp,库是新版,或反之。解决:头文件和 .lib 必须来自同一次编译产物,别混用不同来源。资源里若头文件和库分属不同版本,以库为准换头文件,或整体重编。
6.4 运行时段错误在第一次解析
现象:编译链接都过,运行到 parse 就崩。原因:Debug 工程链接 Release 库,_ITERATOR_DEBUG_LEVEL不一致导致容器内部布局错位。解决:统一 Debug/Release;无法统一时,在工程里定义_ITERATOR_DEBUG_LEVEL=0并接受调试能力下降,仅作临时手段。
6.5 服务端启动即退出无报错
现象:服务端 main 返回 0 但端口没监听。原因:静态库的 HTTP 传输层依赖未初始化,或端口被占用但异常被吞。解决:先用 netstat -ano | findstr 8383 查端口;再在服务端构造后加日志确认走到 listen;检查是否漏链 ws2_32.lib 导致 socket 调用失败但返回值未检查。加返回值检查是最省时间的习惯。
7. 装完怎么验证:三层冒烟与一次干净重建
验证分三层,逐层加码,别跳。第一层是链接验证:空 main 加一个 jsoncpp 头文件,能编能链,证明路径和运行库对齐。第二层是功能验证:跑 4.1 的解析程序,输出 name 和 version 正确,证明库的运行时行为正常。第三层是集成验证:起一个本地 RPC 服务端,用客户端发一条 echo 请求,收到回包且 JSON 结构正确,证明两个库协同工作。
# 第三层验证的检查点:先起服务端,再另开窗口发请求 # 服务端监听后,用 curl 发一条 JSON-RPC 请求做旁路验证 curl -s -X POST http://127.0.0.1:8383 -H "Content-Type: application/json" -d "{\"jsonrpc\":\"2.0\",\"method\":\"echo\",\"params\":[\"ping\"],\"id\":1}"curl 返回里result字段应为ping,error为空。这一步能区分“库链接对了但服务端逻辑没起”和“传输层根本没通”。如果 curl 通而自己的客户端不通,问题在客户端代码;curl 也不通,回去查服务端监听和防火墙。
最后做一次干净重建:删掉工程目录下的 Debug/Release 中间目录,重新生成解决方案,确认不是增量编译掩盖了问题。我吃过亏——增量链接把旧 .obj 留着,换库后看似通过,换台机器全量编译立刻翻车。从那以后每次换第三方库,我都强制走一遍“清中间目录 + 全量重建 + 三层冒烟”,不省这一步。希望帮到你。
本文还有配套的精品资源,点击获取