VS2019静态链接libjsoncpp与libjson-rpc-cpp实战指南
简介面向 Windows 平台 C 开发者尤其是需要在 VS2019 环境下集成 JSON 解析与 JSON-RPC 通信能力的工程师这份资源提供已静态编译完成的 libjsoncpp 与 libjson-rpc-cpp 库可直接用于项目。相比网络上流传的同类包它补齐了缺失的 libjsoncpp 文件并采用 X64 架构编译避免 x86 版本在 64 位工程中无法链接的问题。压缩包共 355 个文件约 20.82MB包含 4 个 lib 静态库、3 个 sln 解决方案、4 个 vcxproj 工程文件以及 27 个 cpp 源文件、25 个头文件、16 个 Python 脚本和若干 json 配置、日志与构建中间文件既可直接取用库文件也能基于工程自行二次编译为 X64 或 x86 版本。已有 475 人学习下载适合希望省去繁琐编译配置、快速搭建 JSON-RPC 服务端与客户端的中高级开发者参考使用。1. 拿到 libjsoncpp 和 libjson-rpc-cpp 静态库先别急着往 VS2019 里拖上周帮一个做桌面端采集工具的朋友收拾构建环境他项目里要用 JSON 做本地配置读写还要跑一套 JSON-RPC 做进程间调用。他图省事从网上随手抓了两个动态库版本的 jsoncpp 和 json-rpc-cpp结果在 VS2019 里一编译就是一堆 LNK2019 未解析外部符号换 Debug/Release 又报运行库不匹配折腾一下午没跑起来。这类坑在 Windows 上太常见了jsoncpp 和 libjson-rpc-cpp 本身不复杂难的是编译期和链接期的运行库、字符集、架构三件事必须对齐。这篇记录的就是我拿到一份已经静态编译好的 libjsoncpp 和 libjson-rpc-cpp 之后怎么在 VS2019 里把它接进工程、怎么配、装坏了怎么救。适合正在做 Windows C 桌面项目、需要本地 JSON 解析和轻量 RPC 的开发者新手能照着走通熟手能对一下版本和参数取舍。2. 环境对齐VS2019 工具集、运行库和架构三件套静态库能不能直接用九成取决于「编译库的那套环境」和「你工程的那套环境」是否一致。jsoncpp 和 libjson-rpc-cpp 都是 C 库静态链接时会把库内部的运行库依赖一起带进来一旦两边对不上链接器就会用 LNK2038、LNK2019 这类错误告诉你「我们不熟」。所以第一步不是写代码而是把环境列清楚。2.1 确认 VS2019 的 MSVC 工具集版本VS2019 默认装的是 v142 工具集但小版本之间也有差异。打开「Developer Command Prompt for VS 2019」敲cl输出第一行会带版本号形如Microsoft (R) C/C Optimizing Compiler Version 19.29.xxxxx for x64。19.29 对应 VS2019 的 16.11 系列这是 v142 的最后一个大版本。静态库如果是用 19.2x 编的通常能兼容如果库是用 VS2022 的 v143 编的链接时大概率报LNK2038: 检测到“_MSC_VER”的不匹配项。我一般会先问清楚库的编译工具集问不到就用dumpbin反查后面 2.3 会讲。提示不要用 VS2019 打开一个用 VS2022 创建的 sln 再降级工具集回退经常留下隐藏的PlatformToolset残留清理不干净会反复报错。2.2 运行库选项必须和静态库一致这是静态链接最容易翻车的地方。VS 工程的「C/C → 代码生成 → 运行库」有四个选项/MT、/MTd、/MD、/MDd。静态库如果是以/MT多线程静态运行库编的你的工程也必须用/MT用/MD就会报LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”。常见做法是拿到静态库先看它带不带libcmt的符号。用dumpbin /directives看库文件里的/DEFAULTLIB指令dumpbin /directives libjsoncpp.lib | findstr DEFAULTLIB如果输出里有LIBCMT说明是/MT系列有MSVCRT就是/MD系列。带d后缀的库如libjsoncppd.lib一般对应 Debug 运行库。我一般会把 Release 工程固定成/MTDebug 工程固定成/MTd和静态库一一对应省得后面反复切。2.3 用 dumpbin 反查架构和工具集架构不对是最隐蔽的坑x64 工程链 x86 的库报错往往不是「架构不符」而是一堆莫名其妙的未解析符号。用下面两条命令把库的底细摸清dumpbin /headers libjsoncpp.lib | findstr machine dumpbin /headers libjson-rpc-cpp.lib | findstr machinemachine (x64)表示 64 位machine (x86)表示 32 位。两个库的架构必须和你的工程平台一致不能一个 x64 一个 x86 混着链。工具集版本则看库里的_MSC_VER相关符号或者直接看库文件所在目录的命名习惯——很多预编译包会写成libjsoncpp-v142-x64-MT这种形式目录名就是最直接的线索。把这三件事对齐之后环境这一关就算过了。下面这张表是我自己整理的环境对照配工程时对着填项目静态库侧VS2019 工程侧不一致的典型报错工具集v142 (19.29)平台工具集 v142LNK2038 _MSC_VER运行库/MT 或 /MTd代码生成 → 多线程(/MT)LNK2038 RuntimeLibrary架构x64解决方案平台 x64LNK2019 大量未解析字符集与库无关Unicode 或 MBCS 均可一般不影响链接3. 把静态库接进 VS2019 工程头文件、库目录、附加依赖项环境对齐后真正把库「接进去」靠的是三处配置头文件搜索路径、库文件搜索路径、附加依赖项。这三处任何一处写错表现都是编译能过、链接失败所以配完一定要做一次完整重建别只做增量编译。3.1 头文件目录和库目录怎么填假设你把预编译包解压到了D:\sdk\json\目录结构大致是include\json\jsoncpp 头文件、include\jsonrpccpp\json-rpc-cpp 头文件、lib\x64\两个 .lib 文件。在 VS2019 里右键工程 → 属性注意顶部「配置」选「所有配置」「平台」选 x64然后「C/C → 常规 → 附加包含目录」填D:\sdk\json\include「链接器 → 常规 → 附加库目录」填D:\sdk\json\lib\x64这里有个细节jsoncpp 的头文件引用方式是#include json/json.h所以附加包含目录要指到include这一层而不是include\json。指错了会报Cannot open include file: json/json.h。libjson-rpc-cpp 的头文件常见引用是#include jsonrpccpp/server.h同理指到include层。注意路径里尽量不要带中文和空格。VS2019 对含空格路径的处理在部分插件下会出问题我一般把 SDK 放在纯英文短路径下比如D:\sdk\。3.2 附加依赖项写哪几个 lib「链接器 → 输入 → 附加依赖项」里填库文件名多个用分号或换行分隔。Release 配置一般写libjsoncpp.lib;libjson-rpc-cpp.libDebug 配置如果预编译包提供了带 d 后缀的版本就写libjsoncppd.lib;libjson-rpc-cpd.lib。这里要留意 libjson-rpc-cpp 的依赖关系它内部依赖 jsoncpp所以链接顺序上 jsoncpp 放在后面通常更稳MSVC 的链接器对顺序不像 GCC 那么敏感但把被依赖的库放后面是通用习惯。如果链接时提示LNK2019: 无法解析的外部符号 ... jsoncpp ...先别怀疑库坏了八成是附加依赖项里漏了libjsoncpp.lib或者库目录指错了一层。我习惯在配完后用「链接器 → 命令行」看一眼最终展开的命令行确认/LIBPATH和库名都在里面。3.3 用一段最小代码验证链接是否打通配置完别急着上业务代码先写个最小验证程序把 jsoncpp 的解析和序列化跑一遍#include json/json.h #include iostream #include sstream int main() { // 构造一个 JSON 对象并序列化 Json::Value root; root[name] demo; root[port] 8080; root[enabled] true; Json::StreamWriterBuilder writer; std::string out Json::writeString(writer, root); std::cout serialized: out std::endl; // 再解析回来验证读写闭环 Json::CharReaderBuilder reader; Json::Value parsed; std::string errs; std::istringstream iss(out); if (!Json::parseFromStream(reader, iss, parsed, errs)) { std::cerr parse failed: errs std::endl; return 1; } std::cout port parsed[port].asInt() std::endl; return 0; }这段代码只用到 jsoncpp目的是先确认「头文件能找到、库能链上、运行库不冲突」这条链路是通的。Json::StreamWriterBuilder和Json::CharReaderBuilder是 jsoncpp 较新版本的写法老版本用的是Json::FastWriter和Json::Reader如果你手上的库版本偏老编译报「未声明的标识符」就换成老写法。跑通输出serialized: {enabled:true,name:demo,port:8080}和port 8080说明 jsoncpp 这一半已经落地。libjson-rpc-cpp 的验证放到第 5 章因为它需要一个 server 和 client 配合单独跑不起来。先把 jsoncpp 这半边的链接打通能排除掉大部分环境问题。4. 避坑与排查静态链接最常见的五类翻车静态库的坑有个共同特点编译期不报链接期才炸而且报错信息往往指向业务代码让人误以为是自己的问题。下面五条是我这几年反复遇到的按「现象 → 原因 → 解决」记下来配环境时对着排。4.1 LNK2038 运行库不匹配现象链接时报LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”(xxx.obj 中)。原因静态库用/MT编工程用/MD或者反过来。静态链接时两套运行库的符号会冲突。解决用 2.2 的dumpbin /directives确认库的运行库类型然后把工程「C/C → 代码生成 → 运行库」改成一致。注意 Debug 和 Release 要分别改VS 默认 Release 是/MD很多人只改了 Debug 忘了 Release。4.2 LNK2019 大量未解析符号现象几十上百条LNK2019: 无法解析的外部符号符号名里带jsoncpp或jsonrpc。原因三种可能——附加依赖项漏了库、库目录指错、架构不一致。架构不一致时符号名会带不同的修饰看起来像「库没链上」其实是链上了但签名对不上。解决先看「链接器 → 命令行」确认/LIBPATH和库名都在再用dumpbin /headers核对架构最后确认附加依赖项里两个库都写了。三步走完基本能定位。4.3 头文件能找到但编译报 C4996现象编译报C4996: Json::FastWriter: 被声明为已否决。原因jsoncpp 新版本把FastWriter、Reader等旧接口标记为 deprecated工程又开了/WX警告当错误。解决要么改用StreamWriterBuilder/CharReaderBuilder新接口要么在包含头文件前加#define JSONCPP_DEPRECATED相关宏抑制但更推荐改代码。我一般直接改新接口旧接口在后续版本里可能被移除。4.4 Debug 能跑 Release 崩现象Debug 配置下程序正常切到 Release 一运行就崩崩在 JSON 解析或 RPC 调用附近。原因Debug 用了/MTd的库Release 却链了/MT的库或者 Debug 链的是带 d 后缀的库、Release 忘了换。运行库混用导致的堆内存管理不一致在 Debug 下被调试堆掩盖Release 下直接暴露。解决把 Debug 和 Release 的「附加依赖项」「库目录」「运行库」三处分别核对确保 Debug 全用 d 版本、Release 全用非 d 版本。我习惯在工程里建两个属性表.propsDebug 和 Release 各挂一个避免手改漏项。4.5 静态库重复定义现象链接报LNK2005: xxx 已经在 libjsoncpp.lib 中定义。原因工程里同时链了静态库和动态库的导入库或者两个不同版本的 jsoncpp 库都被链进来。解决检查附加依赖项只保留一套检查「链接器 → 输入 → 忽略特定默认库」有没有误配。如果是第三方库间接带入了另一份 jsoncpp用/NODEFAULTLIB排除冲突的那份但要确认排除后符号仍能解析。5. 装完怎么验证server/client 跑通才算真落地jsoncpp 单独跑通只证明了一半libjson-rpc-cpp 的价值在于 RPC 调用必须 server 和 client 配合才能验证静态库完整可用。这一章给一套最小可跑的验证方法跑通它这套静态库就算真正接进工程了。5.1 写一个最小 RPC serverlibjson-rpc-cpp 的典型用法是继承AbstractServer或使用HttpServer。下面是一个基于 HTTP 的最小 server暴露一个add方法#include jsonrpccpp/server.h #include jsonrpccpp/server/connectors/httpserver.h #include json/json.h #include iostream class DemoServer : public jsonrpc::AbstractServerDemoServer { public: DemoServer(jsonrpc::AbstractServerConnector conn) : jsonrpc::AbstractServerDemoServer(conn) { // 绑定方法名和实现参数个数要与实现签名一致 this-bindMethod(add, DemoServer::add); } Json::Value add(const Json::Value request) { int a request[params][0].asInt(); int b request[params][1].asInt(); Json::Value result(a b); return result; } }; int main() { jsonrpc::HttpServer httpServer(8383); DemoServer server(httpServer); server.StartListening(); std::cout server listening on 8383 std::endl; std::cin.get(); // 阻塞回车退出 server.StopListening(); return 0; }bindMethod的第一个参数是客户端调用的方法名第二个是成员函数指针。HttpServer构造参数是监听端口这里用 8383 避开常用端口。StartListening之后 server 在后台线程跑主线程用std::cin.get()阻塞住回车后StopListening收尾。这段代码同时用到了 jsoncpp 和 libjson-rpc-cpp能编译链接通过说明两个静态库都接对了。5.2 用 client 发一次调用client 侧可以用HttpClient构造请求也可以直接用 curl 发 JSON-RPC 报文。用 curl 更轻适合快速验证curl -X POST http://127.0.0.1:8383/ -H Content-Type: application/json -d {\jsonrpc\:\2.0\,\method\:\add\,\params\:[3,5],\id\:1}预期返回{jsonrpc:2.0,result:8,id:1}。如果返回Method not found检查bindMethod的方法名是否和请求里的method一致如果连接被拒检查 server 是否真的StartListening成功、端口有没有被占用。这一步跑通说明静态库的 RPC 链路完整可用。5.3 验证清单和收尾习惯我一般按下面这张清单逐项打勾全过才算这套静态库真正落地验证项方法通过标准jsoncpp 序列化跑 3.3 的最小程序输出合法 JSON 字符串jsoncpp 反序列化同上解析回读字段值正确RPC server 启动运行 5.1 程序打印 listening 且不崩RPC 调用curl 发 add 请求返回 result 为两数之和Debug/Release 双跑两个配置各跑一遍均正常无运行库报错最后说个血泪教训静态库这东西最怕「Debug 能跑就以为万事大吉」。我现在的习惯是每接一套新的静态库Debug 和 Release 各跑一遍 5.1 的 server/client两个配置都通过才往业务工程里合。因为运行库和工具集的不一致往往只在 Release 下暴露等业务代码堆上去再发现排查成本翻好几倍。这套 libjsoncpp 和 libjson-rpc-cpp 的静态库本身是干净的坑基本都在工程配置侧把第 2 章的三件套对齐、第 4 章的五类报错记住接进去并不费劲。希望帮到你。本文还有配套的精品资源点击获取