ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

VS2019下静态编译libjsoncpp与libjson-rpc集成指南

VS2019下静态编译libjsoncpp与libjson-rpc集成指南 简介面向Windows平台上的C开发者提供基于Visual Studio 2019环境静态编译完成的JSON解析库和JSON-RPC通信库整体打包为可直接引用的静态链接库文件包含头文件与导入库。针对目前网络上流行的相关编译包存在缺少依赖文件以及仅支持三十二位架构的缺陷此包特意补齐依赖并同时准备好六十四位与三十二位两套构建工程方便开发者按需选用或在此基础上进行二次编译。压缩包内文件总数为三百五十五个大小约二十点八二兆字节主要包含头文件、源文件、对象文件、编译日志、配置文件以及构建辅助脚本目录结构清晰方便查阅与定位模块。目前已有四百七十四人学习使用。除最终库文件外还附带构建配置脚本和许可证说明有助于理解编译流程与后续移植。 做Windows C开发的人十有八九都被JSON解析和跨进程通信这两件事折磨过。标准库不带JSON解析功能RPC就更别想了最常见的路子就是引入libjsoncpp和libjson-rpc这两个开源库。但源码拿回来后你还得自己过一遍CMake配置、编译、链接中间踩的坑一套接一套CMake版本不对、依赖库找不到、运行库冲突、Debug和Release混用……每一步都能让新手折腾大半天。这次分享一套在VS2019下静态编译好的libjsoncpp和libjson-rpc库文件目录整理干净拿到手就能直接集成到项目里用。凡是做C桌面程序、需要本地JSON解析或者打算用JSON-RPC做进程间通信的朋友这篇内容大概率能帮你省掉一个下午的折腾时间。我先把结论放在前面这套库我一直在用编译参数、目录结构、使用方式都验证过下面会把编译过程中的关键参数、集成步骤和遇到的坑全部写清楚。就算你不想直接用现成的库文件跟着操作自己从头编一遍也不难。1. 为什么一定要静态编译这两个库1.1 两个库各管哪摊事先明确概念。libjsoncpp也就是常说的jsoncpp是目前C生态里最流行的JSON解析与序列化库之一。它的核心思路很简单把JSON文本解析成一棵用Json::Value表示的树形结构之后你想读哪个字段、改哪个字段、再把整个结构输出成字符串都在这棵树上面操作。相比直接手写解析器jsoncpp极大降低了JSON处理的心智负担。libjson-rpc-cpp则是云端之上的另外一层。它实现了JSON-RPC协议——简单说就是让两个进程之间通过JSON格式的请求和响应来完成“调用远程方法”这件事。最常见的使用场景是服务端注册几个方法监听TCP端口或HTTP端口客户端连接过来发一段JSON请求服务端执行对应方法后把结果以JSON格式返回。你不用关心底层的socket细节只需要面向协议写业务逻辑。这两个库本身没有强绑定关系但libjson-rpc-cpp依赖jsoncpp来完成消息的解析与组装。所以你需要两个库一起编译、一起提供集成方拿到手才能直接跑。1.2 动态库和静态库的真实取舍有人会问为什么非要静态编译用DLL不行吗那得看你项目的实际部署场景。Windows下用VS2019编译DLL生成时会附带一些额外的运行时DLL依赖比如msvcp140.dll、vcruntime140.dll等。如果你交付的是一个小工具、一个内部系统组件目标机器上不一定装了对应版本的VC运行库。虽然大多数Windows 10/11系统都预装了常见版本但碰到精简版系统、服务器环境或者同时装了多个版本的VSDLL冲突的情况屡见不鲜。静态编译后这些依赖都会被链接进最终的exe。我实际测试过把这套静态库编进去的程序拷贝到一台什么开发环境都没有的Windows 10机器上双击就能跑连VC运行库都不用装。对交付来说省心程度完全不是一个级别。静态库的代价主要是两个一是生成的exe体积会大一些二是如果你还想和其他动态库混用运行库设置必须对齐否则会出现LNK2038这类链接错误。这两点权衡下来对于大多数工具型、系统型项目静态编译的收益远大于代价。对比项动态库DLL静态库LIB部署依赖需要目标机器有对应运行库/适配补丁依赖全部打进exeexe体积较小偏大一般几MB起步链接配置相对麻烦还要带DLL一起发布只需配置include和lib升级替换换DLL即可需要重新编译整个工程更适合场景大型插件架构、模块频繁更新工具类、交付类项目1.3 为什么选VS2019而不是新版本VS2019使用的是v142工具集支持C14/17非常成熟兼容性也稳。很多公司的内部项目还停留在VS2019行业里面流传下来的构建脚本、代码风格也大多基于这一代。另外VS2019社区版免费不像早年专业版要序列号团队成员之间协作完全无障碍。还有一个现实原因这套库我在VS2019下编译并验证过稳定用了很长时间。虽然VS2022的v143工具集也能链接v142编译出来的静态库但在编译环境和交付环境保持一致的场景下VS2019仍然是绕不开的版本。下面所有步骤都以VS2019 x64架构为例。2. 编译前的准备工作和关键参数2.1 源码版本与依赖关系编译之前先把源码版本钉死。我用的分别是jsoncpp1.9.5libjson-rpc-cpp1.3.0这两个版本是一套经过验证的组合。libjson-rpc-cpp在CMake配置阶段会去查找jsoncpp的头文件和库文件它的CMakeLists.txt写得比较“挑”版本太新或太旧都可能出现找不到头文件、链接符号对不上的问题。如果你用最新版jsoncpp去配旧版libjson-rpc-cpp大概率会遇到Json::Value相关的编译错误——因为新版jsoncpp内部头文件路径或API有过调整。源码都从GitHub官方仓库拉别去乱七八糟的镜像站。jsoncpp可以直接下载release的zip包libjson-rpc-cpp同理release页面有对应版本的压缩包解压即可。2.2 决定静态编译的核心CMake开关编译静态库最关键的几个CMake参数我直接列出来BUILD_SHARED_LIBSOFF告诉CMake生成静态库而不是动态库。这个开关在CMake工程里几乎是“静态库标准开关”的存在。JSONCPP_WITH_TESTSOFFjsoncpp默认会连带编译一堆单元测试关掉可以节省大量编译时间也避免生成多余的测试目标。JSONCPP_WITH_PKG_CONFIGOFF不需要生成pkg-config文件Windows下用不到。COMPILE_TESTSOFFlibjson-rpc-cpp的测试开关同样关掉。CMAKE_MSVC_RUNTIME_LIBRARYMultiThreaded对应编译器选项就是/MT这是保证静态编译后不依赖动态运行库的核心开关。另外还有一个容易忽略的点CMake默认在Release配置下可能会继续沿用动态运行库/MD因为你光把BUILD_SHARED_LIBS设为OFF只能控制库本身是静态的但库的CRT依赖是动态的还是静态的得单独用CMAKE_MSVC_RUNTIME_LIBRARY来管。这一步不做编译出来的.lib虽然是静态库但它内部还依赖msvcp140.dll等于白干。2.3 64位还是32位现在Windows环境绝大多数都是x64系统VS2019默认的x64工具链也最成熟。我建议编译x64版本。如果项目非要32位配置上基本完全一样把-A x64换成-A Win32即可但需要留意目标机器的系统位数别编译完拿到32位系统上跑虽然现在很罕见了。3. 完整编译过程从源码到 .lib3.1 编译jsoncppjsoncpp的CMake配置比较简单。下载源码并解压后在源码目录下建一个build文件夹然后打开VS2019的“开发者命令提示符”执行cd /d D:\thirdparty\jsoncpp-1.9.5 mkdir build cd build cmake .. -G Visual Studio 16 2019 -A x64 -DCMAKE_BUILD_TYPERelease -DBUILD_SHARED_LIBSOFF -DJSONCPP_WITH_TESTSOFF -DJSONCPP_WITH_PKG_CONFIGOFF -DCMAKE_MSVC_RUNTIME_LIBRARYMultiThreadedCMake会在build目录下生成jsoncpp.sln解决方案文件。用VS2019打开后把解决方案配置切到Release x64然后右键目标“jsoncpp_static”选择“生成”。这一步顺利的话会在build\lib\Release下得到jsoncpp_static.lib文件。如果你不想用命令行也可以直接用CMake GUI源码路径和生成路径填好之后勾选BUILD_SHARED_LIBS取消勾选其余参数在界面里一样能找到。实际体验下来命令行更快还能保留日志反复核对。3.2 编译libjson-rpc-cpplibjson-rpc-cpp的编译比jsoncpp多一步需要明确告诉它jsoncpp的位置。它默认会尝试通过CMake的find_package去找但如果你像我一样已经把jsoncpp编成静态库最好直接手动指定路径避免它在系统目录里找到别的不匹配版本。在libjson-rpc-cpp源码目录下同样建build文件夹执行cd /d D:\thirdparty\libjson-rpc-cpp-1.3.0 mkdir build cd build cmake .. -G Visual Studio 16 2019 -A x64 -DCMAKE_BUILD_TYPERelease -DCOMPILE_TESTSOFF -DJSONCPP_INCLUDE_DIRD:/thirdparty/jsoncpp-1.9.5/include -DJSONCPP_LIBRARYD:/thirdparty/jsoncpp-1.9.5/build/lib/Release/jsoncpp_static.lib -DCMAKE_MSVC_RUNTIME_LIBRARYMultiThreaded这里JSONCPP_INCLUDE_DIR指向jsoncpp源码目录下的include文件夹JSONCPP_LIBRARY直接指向刚才编译好的静态库文件。这一步如果路径填错CMake会在配置阶段就报错耐心检查路径即可。配置成功后用VS2019打开jsonrpccpp.sln切到Release x64执行生成。libjson-rpc-cpp会生成三个静态库jsonrpccpp-common.lib基础公共库、jsonrpccpp-server.lib服务端、jsonrpccpp-client.lib客户端。需要的库文件就这三个加一个jsoncpp_static.lib一个都不能少。3.3 整理目录结构编译完成后我会把文件和头文件整理成一套干净的结构方便后续复用jsoncpp-rpc/ ├── include/ │ ├── json/ │ │ └── *.h │ └── jsonrpccpp/ │ ├── client/ │ ├── common/ │ └── server/ ├── lib/ │ ├── Release/ │ │ ├── jsoncpp_static.lib │ │ ├── jsonrpccpp-client.lib │ │ ├── jsonrpccpp-common.lib │ │ └── jsonrpccpp-server.lib │ └── Debug/ │ ├── jsoncpp_static.lib │ ├── jsonrpccpp-client.lib │ ├── jsonrpccpp-common.lib │ └── jsonrpccpp-server.lib如果你只需要Release库目录可以精简到只留Release。但我的习惯是编译一套Debug版本留着调试代码时用Release库会有很多困扰变量看不到、断点不生效、优化行为不一致。Debug版的编译过程一模一样只要在VS里把配置切到Debug再生成一遍即可。4. 库文件如何集成到VS2019项目4.1 项目属性配置三步走拿到这套库之后集成到VS2019项目的步骤如下。假设你新建了一个空的控制台程序打开“项目属性”第一步设置头文件目录。“C/C → 常规 → 附加包含目录”加上你的目录\include注意不用细分到json或jsonrpccpp子目录因为代码里包含头文件时写的是#include json/json.h和#include jsonrpccpp/server.h编译器会自己往子目录找。第二步设置库文件目录。“链接器 → 常规 → 附加库目录”加上你的目录\lib\Release。第三步设置依赖库。“链接器 → 输入 → 附加依赖项”手动填入jsoncpp_static.lib jsonrpccpp-client.lib jsonrpccpp-common.lib jsonrpccpp-server.lib这里有一个很重要的注意事项链接顺序和库的依赖关系。通常把被依赖的库放在后面jsoncpp_static.lib放在最后多维依赖时MSVC的链接器对顺序比较挑剔。按上面这个顺序写实测稳定。4.2 运行时库必须统一配置完上面的路径和依赖项之后还要回到“C/C → 代码生成 → 运行库”确认当前项目也选的是多线程(/MT)而不是默认的多线程调试 DLL(/MDd)或多线程 DLL(/MD)。这一点的原理在于静态库内部是按照/MT方式链接的如果你项目本身用/MD那么exe和静态库将使用不同的C/C运行库一个静态版、一个动态版两边在内存分配、堆管理上各搞一套轻则编译告警重则链接直接报LNK2038运行期出现偶发崩溃。这也是static库和动态库混合使用时的第一大坑。我见过不少人在这里卡住明明库文件路径都配对了编译就是报错最后发现是运行库选项没有改。如果你要把这套库集成到已有项目里改这个选项之前最好确认项目里其他第三方库的编译方式如果它们是用/MD编译的那就不建议硬改全局设置而是换一套用/MD编译的库文件。这也是为什么我前面建议Debug库和Release库各留一份真遇到运行库冲突时还能灵活切换。4.3 一段能跑通的测试代码集成完成后用一段最简单的代码验证库是否可用。先测jsoncpp的解析#include iostream #include json/json.h int main() { std::string raw R({name:test,value:42}); Json::Value root; Json::CharReaderBuilder builder; std::string errs; std::istringstream iss(raw); if (!Json::parseFromStream(builder, iss, root, errs)) { std::cerr parse error: errs std::endl; return -1; } std::cout name root[name].asString() std::endl; std::cout value root[value].asInt() std::endl; return 0; }再测libjson-rpc-cpp的最简服务端和客户端。服务端注册一个sayHello方法#include jsonrpccpp/server.h #include jsonrpccpp/server/connectors/tcpsocketserver.h class SampleServer : public jsonrpc::AbstractServerSampleServer { public: SampleServer(jsonrpc::IConnectionHandler conn, jsonrpc::serverVersion_t type) : AbstractServerSampleServer(conn, type) { this-bindMethod(sayHello, SampleServer::sayHello); } void sayHello(const Json::Value request, Json::Value response) { response[result] hello, request[params][name].asString(); } }; int main() { jsonrpc::TcpSocketServer server(127.0.0.1, 8080); SampleServer sample(server, jsonrpc::JSONRPC_SERVER_V2); sample.StartListening(); std::cout server started, press any key to exit... std::endl; std::cin.get(); sample.StopListening(); return 0; }客户端简单调一下这个方法#include jsonrpccpp/client.h #include jsonrpccpp/client/connectors/tcpclientconnector.h int main() { jsonrpc::TcpClientConnector conn(127.0.0.1, 8080); jsonrpc::Client client(conn, jsonrpc::JSONRPC_CLIENT_V2); Json::Value params; params[name] world; try { Json::Value result client.CallMethod(sayHello, params); std::cout result.toStyledString() std::endl; } catch (jsonrpc::JsonRpcException e) { std::cerr e.what() std::endl; } return 0; }先启动服务端程序再启动客户端程序能打印出hello, world就说明整套库链路完全没问题。这个测试值得保留下来以后每次在新环境集成时先拿这段代码验证环境能排除掉一半以上的配置类问题。5. 编译和使用中的常见坑与排查5.1 LNK2038 运行时库不匹配这是使用这套静态库时最常遇到的链接错误报错长这样error LNK2038: mismatch detected for RuntimeLibrary: value MT_StaticRelease doesnt match value MD_DynamicRelease原因就是我在4.2里说的项目运行库设置和库本身的运行库不一致。排查方式很简单打开项目属性把运行库改成多线程(/MT)重新编译即可。如果你的项目因为其他依赖不能改那就只能换一套用/MD编译的库文件别无他法。5.2 无法解析的外部符号error LNK2019: unresolved external symbol public: void __cdecl jsonrpc::....这种情况多半是附加依赖项漏写了。libjson-rpc-cpp有三个库少写一个就会出现这种错误。另一个常见原因是Debug模式下错误地链接了Release库或者反过来。Debug库通常带d后缀编译之前先去lib目录里确认文件名再对着文件名填依赖项即可。5.3 cpp文件加中文注释就报错这个问题在很多VS项目里都会碰到和你用不用这两个库没关系但集成时很容易撞上。现象是cpp文件里一旦出现中文注释编译就报error C2001: 常量中有换行符或者警告C4819有些情况下还报乱码错误。根因是VS2019默认按系统当前代码页去解析源文件如果源文件是UTF-8编码但没有BOM头编译器可能错误地把中文注释的字节流当成别的编码解析从而破坏语法结构。解决办法有两个二选一一是把源文件另存为“UTF-8 with BOM”VS2019里通过“文件 → 高级保存选项”选择这个编码二是在项目“C/C → 命令行 → 附加选项”里加上/utf-8让编译器强制按UTF-8解析。我建议直接加编译选项一劳永逸不用每个文件单独保存编码。5.4 Debug和Release库混用这套库如果用一段时间后你可能会像我一样把Debug和Release库都编译好放在一起。这时注意一个陷阱Debug库和Release库的文件名默认是一样的除非你在CMake里手动给Debug版本加了后缀。如果两个版本都拷贝到了同一个目录后生成的会覆盖先生成的导致Debug和Release模式下链接到同一份文件运行期出现各种诡异行为。我自己的做法是用目录区分lib\Release和lib\Debug分开放然后在VS2019里针对Debug和Release配置分别设置不同的附加库目录这样永远不会混。另外每次重新编译库之后记得确认一遍文件的修改时间确保拷贝的是最新版本。5.5 版本新旧对应不上如果你打算自己重新编译最新版libjson-rpc-cpp需要特别留意它和jsoncpp的版本对应关系。我用的1.9.5 1.3.0组合虽然老但兼容性最好。新版本的libjson-rpc-cpp可能对C标准要求更高可能需要VS2019的较新更新补丁或者要求jsoncpp的头文件位置不同。这里给你一个排查思路编译libjson-rpc-cpp时如果报找不到json/json.h那就是JSONCPP_INCLUDE_DIR没配好如果报Json::Value相关的红波浪线或链接错误那就要检查jsoncpp版本。不要指望最新版爽稳定复现才是重点。写在后面这套VS2019静态编译好的libjsoncpp和libjson-rpc我实际用了挺长一段时间好几个内部工具都是拿它打底做的。最直观的感受就是交付省心。编出来的exe拷到没装开发环境的机器上双击就能跑不需要额外装运行库也不用担心DLL缺失。如果你也打算在自己的项目里用我的建议是不要只拿别人编译好的库文件就跑最好把编译过程也过一遍哪怕花一个小时把CMake命令跑通。因为只有理解了/MT、BUILD_SHARED_LIBS、include和lib目录这些概念遇到问题的时候才不会慌。之后哪怕项目升级到VS2022或者换了别的编译器你也能照葫芦画瓢重新编译一套。这套东西值不值得折腾用过一次就知道了。本文还有配套的精品资源点击获取
返回列表