简介:面向在Windows平台开展C/C++项目并希望接入Redis的开发者,这份预编译的Redis客户端库将hiredis.lib、Win32_Interop.lib及相关头文件打包成套,解决了在Windows下直接使用Redis客户端库的痛点,省去从Linux环境移植、自行编译适配的繁琐环节。资源整体仅20个文件、6.9MB,体积轻量且结构清晰,以lib静态库和h头文件为核心,同时带有exe小工具、dll运行组件及一份说明文本,目录按x86/x64分别组织,开发者可以按需选配。目前已有1700余人在CSDN学习下载,是一套经过验证的Windows可用方案。具体来看,每个架构下均包含Debug与Release两种配置,运行库分别使用/MTD和/MT,兼顾调试便利与发布性能;hiredis作为Redis官方推荐的C客户端库,可高效处理服务器回复,Win32_Interop则补全Windows系统调用与兼容层,使Redis能稳定运行在Windows环境中。借助这套库,开发者可在Visual Studio等编译环境中直接链接调用,快速实现Redis作为缓存、消息中间件或键值存储的核心功能;连同附带的源码与说明文本,还能深入理解hiredis的解析机制和Win32_Interop在系统调用、线程兼容等方面的适配细节,无论用于实际项目交付,还是研究Linux软件向Windows移植的工程实践,都是高性价比的参考资料。 有人在群里问我:“Windows 下有没有现成的 Redis C 客户端库能直接链接?”我一开始想说“有,vcpkg 一把梭”,但看到它后面跟着“含 hiredis.lib、Win32_Interop.lib 及相关头文件”这句,就明白了:这不是随便装个包的事,而是要把一套能稳定跑在 Windows 上的 Redis 客户端静态库真正编译出来、整理好、交给项目用。这套东西我前前后后折腾过好几次,中间踩的坑不比写业务代码少,今天把它完整捋一遍,希望能帮到正在被 POSIX 函数和链接错误折磨的人。
hiredis 是 Redis 官方的 C 语言客户端库,本身是为类 Unix 环境设计的。Linux 下安装它几乎无脑:make && make install,几分钟完事。但换到 Windows,这套流程瞬间失效——hiredis 源码大量使用read/write/close/strcasecmp/ssize_t这类 POSIX 符号,而 Windows CRT 和 Winsock 的命名规则与接口形态跟 Unix 完全不是一回事。直接拿源码编译,光是头文件和链接错误就能让你怀疑人生。这时候就需要 Win32_Interop 这种桥接层,把 POSIX 调用翻译成 Windows API,才能让 hiredis 在 Windows 工具链里顺利活下来。
这篇内容我会围绕以下几条线展开:先讲清楚为什么 hiredis 在 Windows 下编译这么折腾、Win32_Interop 在其中扮演什么角色;再交代我用的工具链和编译参数;然后给出完整的编译步骤、最终交付的包结构;最后用一个实际可运行的 C 示例演示怎么接入项目,并把编译和链接阶段常遇到的报错整理成排查表。适合需要在 Windows 下做 Redis 客户端开发、想把 hiredis 静态库纳入现有 C/C++ 工程、或者对跨平台 C 库移植感兴趣的读者。
1. 为什么这套库值得自己手动折腾一次
1.1 hiredis 在 Windows 下难编译的根源在哪
我们先明确一件事:hiredis 不是“不愿意支持 Windows”,而是它天生站在 POSIX 这一边。源码里随处可见<unistd.h>、<sys/socket.h>、getaddrinfo、close、read、write,这些都是 Unix 环境下的标准接口。Linux 上一切理所当然,Windows 上就成了拦路虎。
具体来说有几个典型的冲突点:
- include 头文件不兼容。
<sys/socket.h>在 Windows SDK 中不存在,对应功能分散在<winsock2.h>里,两者不能简单替换。 - 函数命名规则不同。Windows CRT 给很多 C 标准函数加了前导下划线,比如
read对应_read,write对应_write,close对应_close,strcasecmp对应_stricmp。 - 套接字关闭语义不一致。Unix 里
close()可以直接关闭 socket 描述符,Windows 上必须用closesocket(),用close()关 socket 会导致句柄泄漏甚至程序崩溃。 - 类型定义缺失。
ssize_t这种类型在老版本 MSVC 里没人定义,编译时直接报 C2065 标识符未声明。
这些问题单独拎出来任何一个都好解决,但叠加在一起,就构成了“看着简单、实际难搞”的局面。微软当年为了把 Redis 移植到 Windows,维护了一套带 Win32_Interop 的适配层,本质上是用一个兼容层把上述 POSIX 接口逐个映射到 Windows 原生实现。
1.2 为什么不直接靠 vcpkg 一把梭
我知道你现在肯定想问:都什么年代了,vcpkg install hiredis:x64-windows一行命令不香吗?香,确实香,我在很多普通项目里也会这么干。但这个项目的情况不太一样:
- 目标环境中可能不具备在线安装条件,需要把库文件直接随代码一起分发;
- 你手里可能有一批老代码,它们依赖的是微软 Redis 分支里那个特定版本的 hiredis 和 Win32_Interop 接口;
- 面试或交付场景里,别人给的就是一整个“编译好的库包”,你需要理解里面每部分是谁、干什么用;
- 手动编译能让你完全掌控运行时库、优化选项、架构匹配,绕开 vcpkg 默认配置和项目要求不一致的麻烦。
所以我的观点是:vcpkg 可以作为参考比对的“标准答案”,但真正要输出一套包含 hiredis.lib、Win32_Interop.lib 和目标头文件的交付物,手动编译一次是绕不开的,你也能借此把所有兼容性问题彻底搞清楚。等这一步做完,你会对链接器、静态库、运行时库这些概念有新的理解。
2. 编译前的准备与工具链选择
2.1 需要准备哪些东西
这套流程完全在 Windows 上操作,建议环境如下:
- Windows 10 或 Windows 11,64 位系统;
- Visual Studio 2019 或 2022,安装时勾选“使用 C++ 的桌面开发”工作负载;
- Git,用于拉取源码,也可以用现成压缩包;
- 可选:一个顺手的内网 Git 镜像,方便在源码下载不顺畅时同步仓库,不影响后续任何步骤。
这里强调一个细节:Visual Studio 必须装上“C++ 桌面开发”相关组件,只装 VS Build Tools 也是可以的,但没装 C++ 工具链,后面cl.exe和lib.exe就根本不存在。我第一次编译时就栽在这上面,打开开发者命令提示符后直接提示“不是内部或外部命令”,后来检查发现是当时图省事只装了 C# 工作负载。
2.2 Win32_Interop 到底是干什么的
Win32_Interop 是这整套移植方案里最核心的“翻译官”。它提供了一组头文件和实现,把 hiredis 代码里依赖的 POSIX 接口“改名换姓”成 Windows CRT 或 Winsock 对应的调用。
实际操作中,Win32_Interop.h里会包含类似这样的宏映射:
#define open _open #define read _read #define write _write #define close _close #define strcasecmp _stricmp同时,针对 socket 相关的特殊处理,微软的移植分支会在net.c等文件里额外做#ifdef _WIN32分支,把close映射成closesocket,把网络相关调用统一到 Winsock2 接口上。这样一来,上层 hiredis 代码几乎不用大改,就能在 Windows 下编译、链接、运行。
顺带一提,这个思路不只是为了解决 Redis 问题。很多 Unix 开源库想移植到 Windows,都会走类似的“兼容头文件 + 宏映射 + 专用实现”路线。你搞懂了 Win32_Interop 的用法,以后移植其他 POSIX 库也有章可循。
2.3 版本和架构选择
我把一切构架固定在两个维度上:
| 维度 | 选择 | 说明 |
|---|---|---|
| 架构 | x64 为主,兼容 32 位则额外编译一套 | 现在的服务器和 PC 基本都是 64 位,x64 是主场景;老项目若依赖 32 位 DLL,才需要 Win32 平台版 |
| 运行时库 | 静态链接使用 /MT | 库发布给他人使用时,最好用 /MT 避免目标机器缺 VCRUNTIME140.dll |
| 编译器 | MSVC 默认工具集 | VS2019/2022 自带的 MSVC 均可用,不建议用 MinGW 交叉混用 |
尤其是“运行时库”这一项,我实际踩过一次:我用默认/MD编译的 lib 给另一个用/MT的项目链接,结果链接器报了一堆LNK2038 mismatch detected for 'RuntimeLibrary'。所以后面统一改成/MT,问题一次性消失。这一个参数在最后集成阶段会反复遇到,务必拿小本本记好。
3. 亲手把 .lib 文件编译出来
3.1 源码从哪里拿
这里有两种拿法,我直接给出推荐路径:
第一种,从微软当年的 Redis for Windows 仓库获取整套源码。这是官方移植分支,内部已经带了 hiredis 源码和 Win32_Interop 适配,拿来就能编,最省事。
git clone https://github.com/microsoftarchive/redis.git克隆完成后,进到仓库目录,定位到 hiredis 和 Win32_Interop 的所在位置。不同历史分支的目录层次略有差别,可能在deps/hiredis、deps/Win32_Interop,也可能在src/hiredis、src/Win32_Interop,建议以实际仓库为准。
第二种,单独拉取官方 hiredis 源码,再手动复制 Win32_Interop 的头文件和实现过来套用。这种方式适合你想跟随最新版 hiredis 功能,但对移植调整要求更高,需要自己动手把宏映射加进去。我中途试用过一次,发现 net.c 里的 Windows 兼容分支要自己补,工作量不小,所以后面还是回到第一种方案。
3.2 编译 Win32_Interop.lib
打开“x64 Native Tools Command Prompt for VS 2022”,依次执行:
cd <redis源码目录>\deps\Win32_Interop cl /c /O2 /MT /DWIN32_LEAN_AND_MEAN Win32_Interop.c lib /OUT:Win32_Interop.lib Win32_Interop.obj这里解释下两个细节:
/O2表示启用速度优化,发布库一般都用它;/MT表示使用静态运行时库,这样生成的 lib 在目标机器上不依赖 VCRUNTIME140.dll;lib /OUT:的作用是把编译出的.obj打包成.lib,这一步看起来简单,但实际是静态库产物从“零散目标文件”走向“可分发交付物”的关键动作。
如果源码目录里 Win32_Interop 依赖多个.c文件,就逐个编译再一起打包,命令类似:
cl /c /O2 /MT /DWIN32_LEAN_AND_MEAN Win32_Interop.c win32_helpers.c lib /OUT:Win32_Interop.lib Win32_Interop.obj win32_helpers.obj具体文件清单以你拿到的仓库为准。
3.3 编译 hiredis.lib
接着编译 hiredis 本体。进入 hiredis 源码目录后执行:
cd <redis源码目录>\deps\hiredis cl /c /O2 /MT /I.. /I..\Win32_Interop /DUSE_WIN32_INTEROP hiredis.c read.c sds.c async.c net.c lib /OUT:hiredis.lib hiredis.obj read.obj sds.obj async.obj net.obj这里的要点在于:
/I..和/I..\Win32_Interop分别指定了 hiredis 头文件所在目录、Win32_Interop 头文件所在目录;/DUSE_WIN32_INTEROP定义了预处理宏,这很重要,hiredis 源码里只有看到这个宏才会启用#include <Win32_Interop.h>的路径;- 编译对象包括
net.c,因为网络部分是 hiredis 的重头戏,也是移植适配最密集的地方。
编译结束后,当前目录会出现一堆.obj文件和两个刚生成的.lib。如果你编译多个架构版本,建议分别建x64和Win32两个输出目录,不然.obj互相覆盖,最后链接时会发现符号完全对不上。
3.4 把交付包整理干净
库这种东西,光给一个.lib文件没有意义,别人根本不知道怎么调。完整的交付包应该按下面的结构组织:
redis-windows-client/ ├── include/ │ ├── hiredis.h │ ├── read.h │ ├── sds.h │ ├── async.h │ ├── hiredis_win32.h (如源码中存在,则一并带上) │ ├── Win32_Interop.h │ └── Win32_Interop_win32.h ├── lib/ │ ├── x64/ │ │ ├── hiredis.lib │ │ └── Win32_Interop.lib │ └── Win32/ │ ├── hiredis.lib │ └── Win32_Interop.lib └── README.md顺手写一个简短的README.md,注明编译工具链版本(VS2022、MSVC v143)、运行时库类型(/MT)、适用架构(x64/Win32),以及链接时需要的附加依赖(Ws2_32.lib)。这些信息看起来琐碎,但实际分发时比 100 行代码注释还有用。
3.5 如果你想走捷径,再用 vcpkg 对比验证一次
如果你在普通场景下只需要一个能用的 hiredis,不想折腾上面的过程,可以这样:
vcpkg install hiredis:x64-windows-static注意这里的x64-windows-statictriplet,表示静态链接、x64 架构,和前面手动编译时用/MT的语义是对齐的。装完以后,vcpkg 会自动配置好头文件和库目录,你需要在项目属性里额外引用它生成的hiredis.lib。
不过要提醒一下:vcpkg 的官方 port 并不会帮你引入 Win32_Interop,它默认提供的是完整适配后的 hiredis。用它做日常开发很舒服,但如果你想精确复刻“含 hiredis.lib、Win32_Interop.lib”这套交付物、或者需要把它嵌入老旧工程,手动编译仍是更可控的路线。
4. 在 C/C++ 项目中正确接入这套库
4.1 项目配置路径和依赖项
假设我已经把上一节整理好的include/、lib/x64/目录放到项目旁边,接下来打开 Visual Studio 项目属性页:
- VC++ 目录 -> 包含目录,添加
include路径; - VC++ 目录 -> 库目录,添加
lib\x64路径; - C/C++ -> 预处理器 -> 预处理器定义,确认添加
USE_WIN32_INTEROP(如果源码调用了该宏); - 链接器 -> 输入 -> 附加依赖项,追加:
hiredis.lib Win32_Interop.lib Ws2_32.lib其中Ws2_32.lib是 Windows 套接字编程的基础库,没有它,hiredis 里所有依赖 Winsock 的符号都链接不过去。这也是我最常碰到的一个坑:只加了 hiredis.lib 和 Win32_Interop.lib,忘了 ws2_32,结果报了一堆__imp_WSAStartup、__imp_getaddrinfo未定义。
4.2 一个能跑的最小示例
下面是一段完整的 C 语言测试代码,演示连接本地 Redis、写入一个键并读取回来:
#include <winsock2.h> #include <stdio.h> #include <hiredis/hiredis.h> int main(void) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup failed\n"); return 1; } redisContext *ctx = redisConnect("127.0.0.1", 6379); if (ctx == NULL || ctx->err != 0) { if (ctx) { printf("Redis connection error: %s\n", ctx->errstr); redisFree(ctx); } else { printf("Cannot allocate redis context\n"); } WSACleanup(); return 1; } redisReply *reply = redisCommand(ctx, "SET name hello"); if (reply == NULL) { printf("SET command failed: %s\n", ctx->errstr); redisFree(ctx); WSACleanup(); return 1; } freeReplyObject(reply); reply = redisCommand(ctx, "GET name"); if (reply != NULL && reply->type == REDIS_REPLY_STRING) { printf("GET name = %s\n", reply->str); } freeReplyObject(reply); redisFree(ctx); WSACleanup(); return 0; }这段代码有几个细节值得注意:
- 必须在调用任何 Redis 命令之前执行
WSAStartup,这是 Windows 下使用 Winsock 的前置条件。面试时经常有人问“为什么我的 Windows 程序一跑 socket 就崩”,八成是少了这一步; - 每次拿
redisCommand的返回值都要用freeReplyObject释放,不然内存泄漏很隐蔽,长时间跑容易把内存吃满; - 连接失败时先判断
ctx是否为空,避免对空指针访问ctx->errstr。
编译命令如果是命令行模式,可以这样:
cl /MT test_redis.c /I include /link /LIBPATH:lib\x64 hiredis.lib Win32_Interop.lib Ws2_32.lib这样生成test_redis.exe,运行之前确保本机或远程有个 Redis 实例在监听 6379 端口。我本地测试时用的是 Docker 起的 Redis,简单方便,不影响这套客户端库的验证。
4.3 静态库与运行时库的匹配问题
很多人在这一步才遇到真正的“连环坑”:库是 /MT 编译的,但自己的项目默认 /MD,于是链接器开始报运行时库冲突。规则很简单:
- /MT 编译的库,使用者也必须尽量 /MT,否则 CRT 符号交叉引用;
- /MD 编译的库,使用者应该保持 /MD;
- 项目里所有参与链接的静态库,运行时库选项要尽量一致。
如果你的项目受制于插件体系必须用 /MD,那我建议回头重新编译一套 /MD 版的 hiredis.lib 和 Win32_Interop.lib,各自打进lib\x64或lib\Win32子目录里,并在 README 中标注清楚。用一个统一样式管理多套静态库,后期切架构、切运行库都方便得多。
5. 常见报错与排查技巧实录
5.1 编译和链接期典型报错速查
下面是我实际遇到过且频率很高的错误,整理成一张速查表:
| 报错信息 | 常见原因 | 解决办法 |
|---|---|---|
| LNK2019:无法解析的外部符号 read/write | 只链接了 hiredis.lib,没链接 Win32_Interop.lib | 在附加依赖项里添加 Win32_Interop.lib |
| 无法解析的外部符号 __imp_WSAStartup | 缺少 Windows Socket 库 | 添加 Ws2_32.lib |
| LNK2038:RuntimeLibrary 不匹配 | 库是 /MT 编译,项目是 /MD,或反之 | 把项目的运行时库改为与库一致 |
| C1083:无法打开包括文件 Win32_Interop.h | 头文件目录没加 | 在包含目录里加 Win32_Interop 所在路径 |
| C2065:“ssize_t”未声明 | 老版本 SDK 缺少类型定义 | 在代码或预编译头里补充typedef intptr_t ssize_t;(或按平台用 SSIZE_T) |
| C2011:结构类型重定义 | 混用了 windows.h 和 winsock2.h | 强制在代码最前面#include <winsock2.h>,并定义WIN32_LEAN_AND_MEAN |
| 链接报错找不到 libcmt.lib 或 libcmtd.lib | 使用了 /MT 但机器上缺少对应 CRT 库 | 确认安装了“适用于最新 v142/v143 生成工具的 C++ MFC(x86 和 x64)”等可选组件 |
所谓的符号导出,决定代码能否顺利引用前者,以上问题中,头文件路径、库路径、运行时库一致性三项占了几乎九成问题。
5.2 链接通过但运行时报错的排查思路
链接成功后不等于万事大吉。程序一启动就崩或者在调用redisConnect时挂掉,按下面顺序排查:
- 确认
WSAStartup是否被调用过。很多人测试时直接抄了 Linux 示例代码,根本没想到 Windows 需要先初始化 Winsock; - 确认 Redis 服务端真的在跑,且端口是 6379;可以用命令行工具
telnet 127.0.0.1 6379先手动验证一下; - 确认本机防火墙没有拦截本地回环连接,一般不会,但公司安全软件有可能这么做;
- 如果
getaddrinfo或 DNS 相关调用异常,考虑是否用了过旧的 SDK 且没链接正确的Ws2_32.lib。
我在一次项目里遇到过很诡异的情况:程序在开发机一切正常,拷到服务器上却报0xc000007b应用无法正常启动。查了半天,发现是 32 位程序链接了 64 位静态库,平台位数不匹配导致一系列符号调用错误。从那以后我每次拿到预编译库都会先跑一次dumpbin /headers,看一眼machine (x64)还是machine (x86),十秒钟的事,能省几小时排查时间。
5.3 验证 .lib 文件的内部结构
最后分享一个很实用的做法:用dumpbin工具检查编译出的库文件是否正常。
dumpbin /headers hiredis.lib头部信息里会明确显示目标机器类型、COFF 符号表结构等关键参数。再用:
dumpbin /exports hiredis.lib如果没有导出表,说明它是个静态库,这是正常现象;如果希望某些函数被外部引用,注意确认redisConnect、redisCommand、freeReplyObject这些符号确实存在。虽然看起来多了一步操作,但这一步能让我这些年来在分发库、接手旧库时少踩无数坑。
6. 从这次手工编译里我学到的东西
第一点体会:不要害怕“编译库”这种听起来底层的工作。它比写业务逻辑更枯燥,但对理解工程整体环境特别有帮助。等你亲手把 hiredis.lib 和 Win32_Interop.lib 编出来,再放到一个干净的 Visual Studio 工程里链接通过,你脑子里的“平台差异”“运行时库”“符号解析”这些模糊概念会全部变得具体。
第二点体会:静态库的治理是门细活。不要只交付一个 .lib,一定要配套头文件、README、编译参数说明,最好按架构和运行时库分目录。这既是职业习惯,也是让后来人少骂你的关键。
第三点体会:库的版本对齐真的很重要。hiredis 官方版本和微软移植分支版本存在差异,如果你的项目后续要用到 hiredis 的新功能,就得评估是升级整个移植分支,还是自己往旧分支里搬到新代码。我一般建议:线上长期维护的项目锁定一个已验证过的版本,别频繁追新。
在你开始动手之前,我再给你一个可复现的快速验证流程:先在一个干净目录里把源码下载好,按第 3 节的命令把两个库编出来,再用第 4 节的最小示例跑通连接,然后写几个简单的 Redis 操作测一遍。整个过程熟练后大概一小时能完成,第一次做可能半天,但绝对值得。
如果你最后发现某个版本编译不过去,先别急着改源码。检查一下是不是源码里没有包含 Win32_Interop 的适配头文件,或者编译命令行缺少-DUSE_WIN32_INTEROP宏。这属于 90% 以上“编译失败”的真正原因,其次才是头文件路径、运行库匹配这些基础问题。
最后再分享一个救命级的小习惯:每次拿到别人给的预编译库,我会先花三十秒执行dumpbin /headers和dumpbin /exports,确认架构、运行时库、导出符号三件事,然后再往项目里放。这一步看似多余,实际上能帮你避开绝大多数“为什么链接不过”“为什么一运行就崩”的低级陷阱。希望这篇内容能让你少走我已经走过的弯路。
本文还有配套的精品资源,点击获取