简介:这份面向Windows平台C/C++开发者的libssh 0.10.3静态库,由VC2015编译,适合需要在自有程序中集成SSH远程登录、命令执行或文件传输能力的项目,可减少对目标环境动态库的依赖。压缩包共10个文件,以7个.h头文件和1个.hpp声明接口,配合2个.lib静态库,整体约636KB,并区分debug与release及vc140目录,便于按构建配置选用。已有177人学习下载,说明其在Windows下集成libssh的场景中有一定参考价值。包内头文件与库文件相互配套,读者可据此完成包含路径和链接器输入设置,快速验证SSH连接、密钥认证与错误码处理;调试版和发布版分目录存放,也方便在开发与部署阶段切换。使用时仍需留意VC++运行时依赖与安全配置,适合具备一定C/C++基础、希望减少环境适配成本的开发者参考。
1. 从一台只装了 VS2015 的机器说起:libssh-0.10.3 静态库不是解压就能用
某次给一个 Windows 桌面工具加 SSH 能力,工程限定在 VS2015、x64、Release 下编译,目标是把 libssh-0.10.3 编成静态库,最终只带一个 ssh.lib 和一组头文件进工程,不额外分发 ssh.dll。Release 链接一次通过,切到 Debug 运行就崩,查了半天才发现 OpenSSL 静态库用的是 /MT,主程序却是 /MDd。类似环境在运维工具、文件传输客户端、批量执行器里很常见:版本不复杂,坑集中在编译器版本、运行库和依赖库链路上。装不好最直接的表现就是 LNK2019、LNK2038,或者编译过了、一握手就异常。下面按一台干净机器从零走一遍,适合需要 ssh.lib 静态链接、又不想在目标机补 DLL 的开发者。
2. 环境准备:vc2015 与 OpenSSL 静态依赖对齐清单
这套编译链的核心不是 libssh 本身,而是 vc2015 的 v140 工具集、CMake 生成器、OpenSSL 静态库和 zlib 静态库四者必须同平台、同运行库。libssh-0.10.3 的源码里带 CMake 工程,生成 ssh.lib 的过程并不绕,但 CMake 找 OpenSSL 时对目录结构比较敏感,找错一次就会把 x86 库塞进 x64 工程,后面所有链接错误都从这里长出来。我一般先把目录约定死,再打开命令行,避免在属性页里来回改路径。
2.1 安装 VS2015 时勾选哪些组件
VS2015 安装器里选“自定义”,至少勾选“编程语言”下的 Visual C++,“Windows 和 Web 开发”下的 Windows 10 SDK 或 Windows 8.1 SDK,以及“通用 Windows 应用开发工具”里需要的 C++ 工具。实际编译 ssh.lib 只依赖 v140 编译器、Windows SDK 和 C 运行时头文件,不需要额外工作负载。装完后从开始菜单找“VS2015 x64 本机工具命令提示符”,不要用普通 cmd,否则cl可能指向别的版本,或者默认生成 32 位目标文件。
:: 在 VS2015 x64 本机工具命令提示符里执行 cl where cmakecl不带参数会输出编译器横幅,版本号以 19.00 开头,说明用的是 VS2015 的 v140 工具集;如果输出 19.1x 或 19.2x,说明环境混进了更新的 MSVC。where cmake用来确认 CMake 来自哪个目录,常见做法是装一个 3.x 版本并加入 PATH,不要和 Visual Studio 自带的旧 CMake 混用。注意命令行窗口标题里要能看到“x64”,否则后面生成的 ssh.lib 是 32 位的,主工程一链接就报计算机类型冲突。
提示:VS2015 有 Update 3 和非 Update 3 的差异,静态库工程尽量统一到 Update 3;如果构建机不止一台,把
cl的完整版本号记在构建脚本注释里,后面排查 LNK2038 会省很多时间。
2.2 依赖目录怎么摆:include、lib、bin 分开
OpenSSL 和 zlib 不要直接丢进 libssh 源码目录。常见做法是在盘上建一个 third_party,按“库名/平台/配置”三层分开,这样 CMake 的OPENSSL_ROOT_DIR和ZLIB_ROOT才有稳定指向。下面是一个够用的目录约定,Release 和 Debug 分开摆,x64 和 x86 不共用。
# 目录只表达层级,实际路径按自己的构建盘调整 D:/third_party/openssl/x64/release/ include/openssl/ssl.h lib/libssl.lib lib/libcrypto.lib D:/third_party/zlib/x64/release/ include/zlib.h lib/zlib.lib D:/build/libssh-0.10.3/ CMakeLists.txt D:/build/libssh-out/ D:/build/libssh-install/OpenSSL 1.1.1 系列在 Windows 下常见的静态库名是libssl.lib和libcrypto.lib,OpenSSL 1.0.2 系列常见的是ssleay32.lib和libeay32.lib。两种命名对应不同的头文件和 API 细节,不要在一个工程里混着链。zlib 的静态库一般叫zlib.lib或zlibstatic.lib,以实际产物为准。D:/build/libssh-out是 CMake 的构建目录,libssh-install是安装目录,源码目录和构建目录分离,后面 CMake 缓存出问题可以直接删 out,不碰源码。
2.3 先确认运行库:/MD 还是 /MT 要在编译 libssh 前定死
静态库最容易翻车的地方不是编译,而是运行库。VS2015 工程默认 Release 用/MD、Debug 用/MDd,如果 OpenSSL 静态库是/MT编的,链接时就会报LNK2038: 检测到“RuntimeLibrary”的不匹配项,或者运行阶段出现堆损坏。libssh 静态库本身也要跟主程序一致,不能主程序/MD、ssh.lib 用/MT。我一般先用dumpbin看 OpenSSL 的默认库指令,再决定要不要重编依赖。
:: 检查静态库默认链接的运行库,MSVCRT 通常对应 /MD,LIBCMT 对应 /MT dumpbin /directives D:\third_party\openssl\x64\release\lib\libcrypto.lib | findstr /i "MSVCRT LIBCMT"如果输出里是DEFAULTLIB:MSVCRT,说明这个 OpenSSL 静态库走动态 CRT,和 VS2015 默认的/MD能对上;如果是DEFAULTLIB:LIBCMT,就要么把主工程改成/MT,要么重新编一份/MD的 OpenSSL。zlib 和 libssh 也按同样规则检查,不要只看一个库。常见做法是统一/MD,因为很多预编译 OpenSSL 静态库默认就是动态 CRT;如果目标机不能装 VC++ 2015 运行库,再考虑全/MT,但那样所有依赖都要重编,成本更高。
3. 生成 ssh.lib:libssh-0.10.3 的 CMake 开关逐项拆解
libssh-0.10.3 的 CMake 工程支持多配置生成器,用Visual Studio 14 2015 Win64生成的就是 VS2015 工程。关键是把BUILD_SHARED_LIBS关掉,让 target 产出静态库;把 OpenSSL 和 zlib 的根目录指到刚才约定的 third_party;再把 GSSAPI 这类当前环境没有的依赖关掉,减少 CMake 在配置阶段乱找。下面这套命令在 x64 本机工具命令提示符里执行,路径按自己的盘改。
3.1 解压源码并建立 out-of-source 构建目录
set SRC=D:\build\libssh-0.10.3 set OUT=D:\build\libssh-out set PREFIX=D:\build\libssh-install cmake -S "%SRC%" -B "%OUT%" -G "Visual Studio 14 2015 Win64" ^ -DBUILD_SHARED_LIBS=OFF ^ -DWITH_STATIC_LIB=ON ^ -DOPENSSL_ROOT_DIR="D:\third_party\openssl\x64\release" ^ -DZLIB_ROOT="D:\third_party\zlib\x64\release" ^ -DWITH_ZLIB=ON ^ -DWITH_GSSAPI=OFF ^ -DCMAKE_INSTALL_PREFIX="%PREFIX%"-S和-B分别指定源码目录和构建目录,要求 CMake 3.13 以上;如果构建机上的 CMake 比较老,可以进到 out 目录执行cmake "%SRC%" -G "Visual Studio 14 2015 Win64",效果一样。-G必须带 Win64,带成Visual Studio 14 2015默认是 32 位。BUILD_SHARED_LIBS=OFF是生成静态库的主开关,WITH_STATIC_LIB=ON在部分 libssh 版本里用于同时准备静态库辅助目标,实际产物仍以ssh.lib为准。OPENSSL_ROOT_DIR指向包含 include 和 lib 的那一层,不是直接指到 lib 目录;ZLIB_ROOT同理。WITH_GSSAPI=OFF是为了避免 CMake 去找 Kerberos 头文件,当前只做 SSH 客户端静态库时通常不需要。
注意:Visual Studio 生成器是多配置生成器,命令行里写
-DCMAKE_BUILD_TYPE=Release不会真正决定产物配置,真正生效的是后面cmake --build --config Release。如果只在 CMake 阶段设了 Release,后面编译仍可能落到 Debug 目录,最后拿错 ssh.lib。
3.2 关键开关背后的取舍:静态库、zlib 和调试符号
BUILD_SHARED_LIBS=OFF之后,libssh 的 target 会产出ssh.lib,而不是ssh.dll加导入库。静态库把 libssh 的目标代码直接放进主程序,部署时不用再带 ssh.dll,但 OpenSSL 和 zlib 的静态库也要一起链进去,最终 exe 体积会明显变大。WITH_ZLIB=ON打开 zlib 支持,SSH 传输压缩会用到;如果当前工程完全不需要压缩,也可以关掉,但很多服务端默认协商里会带 zlib,关了以后行为要自己确认。WITH_GSSAPI=OFF是为了减少依赖,GSSAPI 在 Windows 上还要额外的 Kerberos 库,静态链接会更麻烦。
Debug 和 Release 产物目录不同,libssh 的静态库默认不会在文件名后面加d,所以libssh-out\Debug\ssh.lib和libssh-out\Release\ssh.lib可能同时存在。常见误用是把 Debug 工程的库目录指向 Release 目录,编译能过,运行就出现迭代器调试级别不一致。我的习惯是安装目录也按配置拆开,例如libssh-install\x64\release和libssh-install\x64\debug,后面在 VS2015 属性页里按配置切换。
3.3 编译、安装并核对 ssh.lib 的机器类型与符号
cmake --build "%OUT%" --config Release --target install dir "%PREFIX%\lib" dumpbin /headers "%PREFIX%\lib\ssh.lib" | findstr /i "machine" dumpbin /symbols "%PREFIX%\lib\ssh.lib" | findstr /i "ssh_new"--config Release指定编译 Release,--target install会把头文件、库文件安装到CMAKE_INSTALL_PREFIX下面。dir看安装目录里是否有ssh.lib和include\libssh。dumpbin /headers里的 machine 应该是x64,如果显示x86,说明生成器选错了。dumpbin /symbols找ssh_new,能找到说明静态库里有实际的导出符号,而不是空的导入库。安装完成后检查libssh-install\include\libssh\libssh.h是否存在,后面工程包含目录要指到include,代码里写#include <libssh/libssh.h>。
如果cmake --build报找不到 OpenSSL,先看 out 目录里的CMakeCache.txt,里面会记录OPENSSL_CRYPTO_LIBRARY和OPENSSL_SSL_LIBRARY的最终路径。缓存一旦写错,后面改命令行变量不一定覆盖,常见做法是删掉整个 out 目录重新生成。路径里尽量不要带中文和空格,VS2015 的某些自定义构建步骤对空格处理不如新版本稳。
4. 接入工程:ssh.lib 在 VS2015 里的头文件与链接配置
静态库编出来只是第一步,真正容易卡住的是主工程属性。VS2015 工程要同时设置包含目录、库目录、预处理器宏和附加依赖项,四项缺一项都可能报 LNK2019。尤其LIBSSH_STATIC这个宏,如果没定义,libssh 头文件会按 DLL 导入的方式声明函数,链接器去找__imp_ssh_new,而静态库里只有ssh_new,错误看起来像库没链上,实际是宏的问题。
4.1 在 vcxproj 里固定 Release/x64 的包含目录和库目录
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> <PreprocessorDefinitions>LIBSSH_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>D:\build\libssh-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> <AdditionalLibraryDirectories>D:\build\libssh-install\lib;D:\third_party\openssl\x64\release\lib;D:\third_party\zlib\x64\release\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalDependencies>ssh.lib;libssl.lib;libcrypto.lib;zlib.lib;ws2_32.lib;crypt32.lib;advapi32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup>这段可以直接写进 vcxproj,也可以照着手动改属性页。LIBSSH_STATIC放在预处理器定义里,告诉头文件当前是静态链接。AdditionalIncludeDirectories指到安装目录的include,不是include\libssh,因为代码里带libssh/前缀。AdditionalLibraryDirectories同时包含 libssh、OpenSSL、zlib 三个库目录。AdditionalDependencies里ssh.lib放最前,后面跟 OpenSSL 和 zlib,再补 Windows 系统库。OpenSSL 如果是 1.0.2,库名要换成ssleay32.lib;libeay32.lib,不要照抄。
4.2 一个最小程序:先验证 ssh_new 和 ssh_version 能跑
#include <winsock2.h> #include <libssh/libssh.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") int main(void) { WSADATA wsa; // libssh 在 Windows 上依赖 Winsock,先初始化再创建会话 if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { fprintf(stderr, "WSAStartup failed\n"); return 1; } ssh_session session = ssh_new(); // 创建会话对象,验证静态库链路 if (session == NULL) { fprintf(stderr, "ssh_new failed\n"); WSACleanup(); return 1; } printf("libssh version: %s\n", ssh_version(0)); // 0 表示取当前库版本 ssh_free(session); WSACleanup(); return 0; }ssh_new()只创建会话对象,不发起网络连接,适合做链接和运行时初始化检查。ssh_version(0)返回 libssh 的版本字符串,参数 0 表示按当前库版本返回。WSAStartup放在最前面,是因为 Windows 下 libssh 的 socket 调用依赖 Winsock 初始化;WSACleanup在退出前调用。编译成 x64 Release,运行后应该看到 0.10.3 相关版本输出。如果这里报unresolved external symbol __imp_ssh_new,先回头检查LIBSSH_STATIC有没有定义,而不是急着换库。
4.3 链接顺序和系统库:libssl、libcrypto、ws2_32 不能少
静态 libssh 依赖 OpenSSL 的 SSL/TLS 实现和 zlib 的压缩函数,主工程只写ssh.lib是不够的。链接器输入里把ssh.lib放前面,libssl.lib、libcrypto.lib、zlib.lib放后面,系统库再补ws2_32.lib、crypt32.lib、advapi32.lib、user32.lib。crypt32.lib和advapi32.lib是 OpenSSL 静态库在 Windows 下常见的系统依赖,缺了会报证书或注册表相关符号未解析。Debug 配置要换成对应的 Debug OpenSSL 库,不要用 Release 库硬链。
运行时还要注意,静态链接 libssh 不等于整个 exe 完全免依赖。如果依赖库和主程序都用/MD,目标机仍然需要 VC++ 2015 运行库;如果要做成绿色单文件,所有静态库都要用/MT重新编一遍,包括 OpenSSL、zlib 和 libssh。这个取舍要在项目初期定,不要等发布前才发现目标机缺vcruntime140.dll。
5. 避坑清单:libssh-0.10.3 配 vc2015 最容易翻车的 5 个点
这套组合的报错大多集中在链接阶段,但根因分散在运行库、平台、宏和 CMake 缓存里。下面五条是实际构建里反复出现的翻车点,每条按现象、原因、解决顺序写,遇到类似错误可以先对号入座。
5.1 运行库不匹配:LNK2038 与 _ITERATOR_DEBUG_LEVEL
现象:链接时出现LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”,或者_ITERATOR_DEBUG_LEVEL的值不匹配,Release 能过、Debug 崩溃。原因通常是 OpenSSL 或 zlib 静态库用/MT编的,而 VS2015 主工程用/MD;也可能是 Debug 工程链了 Release 库。解决方法是统一运行库:Release 全部/MD,Debug 全部/MDd,用dumpbin /directives分别检查libcrypto.lib、zlib.lib和ssh.lib的DEFAULTLIB。如果 OpenSSL 只有/MT版本,就重编一份/MD的,不要在主工程里强行改/MT去迁就一个库,后面还会撞别的依赖。
5.2 找不到 OpenSSL 符号:链接顺序与库名
现象:链接报unresolved external symbol __imp_SSL_CTX_new、EVP_DigestInit_ex或CRYPTO_malloc。原因可能是只链了ssh.lib,没链libssl.lib、libcrypto.lib;也可能 OpenSSL 是 1.0.2,库名实际是ssleay32.lib、libeay32.lib;还有一种情况是 CMake 找到了 x86 的 OpenSSL,主工程却是 x64。解决方法是先看CMakeCache.txt里OPENSSL_SSL_LIBRARY的路径,确认平台和库名;再把libssl.lib、libcrypto.lib加进附加依赖项,顺序放在ssh.lib后面;最后用dumpbin /symbols看静态库里有没有对应符号。不要用#pragma comment(lib, "ssl.lib")猜库名,Windows 下 OpenSSL 的库名随版本变。
5.3 LIBSSH_STATIC 没定义:DLL 导入符号
现象:明明链接了ssh.lib,还是报unresolved external symbol __imp_ssh_new或__imp_ssh_options_set。原因是 libssh 头文件在没定义LIBSSH_STATIC时按 DLL 导入声明函数,符号名带__imp_前缀,静态库里没有这种符号。解决方法是在 C/C++ 预处理器定义里加LIBSSH_STATIC,清理工程后重新生成,确保不是增量编译残留。如果同时还在工程里定义了LIBSSH_EXPORTS,要去掉,那个宏是编 libssh 本身时用的,不是给调用方用的。
5.4 32/64 位混用与 Debug/Release 混用
现象:LNK1112: 模块计算机类型“x64”与目标计算机类型“x86”冲突,或者编译通过、运行时在ssh_new里崩溃。原因常见于 CMake 生成器用了 Win64,但 VS2015 工程平台是 Win32;或者库目录里 Release 和 Debug 共用一个路径,Debug 工程拿到了 Release 的 ssh.lib。解决方法是在目录名里强制带平台和配置,例如libssh-install\x64\release,属性页按Release|x64、Debug|x64分别设置;再用dumpbin /headers ssh.lib | findstr machine确认是 x64。不要依赖文件名区分 Debug,libssh 默认不给你加d后缀。
5.5 CMake 缓存污染与生成器残留
现象:改了OPENSSL_ROOT_DIR之后,CMake 仍然提示找不到 OpenSSL,或者找的还是旧路径;有时候重新生成后ssh.lib跑到意想不到的目录。原因是 out 目录里的CMakeCache.txt保存了上一次的变量,命令行新传的值不一定覆盖缓存;也可能在源码目录里直接跑过 CMake,留下了一堆生成文件。解决方法是坚持 out-of-source 构建,改依赖路径后直接删掉整个 out 目录重新生成;路径避免中文和空格;如果之前用错生成器,不要手工改 cache,删掉重来更快。构建脚本里可以把rmdir /s /q "%OUT%"放在 CMake 之前,作为可重复构建的固定步骤。
6. 装完验证:ssh.lib 从符号表到最小程序的四层检查
拿到ssh.lib后不要直接塞进业务工程,先按四层检查走一遍,能把大部分链接问题挡在前面。第一层看文件:libssh-install\lib\ssh.lib是否存在,include\libssh\libssh.h是否完整。第二层看机器类型和符号:dumpbin /headers确认 x64,dumpbin /symbols找ssh_new、ssh_options_set。第三层用最小程序编译链接,第四层运行看版本输出。
:: 四层检查里的前两层,路径按实际安装目录替换 dumpbin /headers D:\build\libssh-install\lib\ssh.lib | findstr /i "machine" dumpbin /symbols D:\build\libssh-install\lib\ssh.lib | findstr /i "ssh_new ssh_options_set"| 检查项 | 命令或动作 | 期望结果 |
|---|---|---|
| 文件存在 | dir libssh-install\lib\ssh.lib | 文件大小非 0 |
| 机器类型 | dumpbin /headers ssh.lib | 显示 x64 |
| 关键符号 | dumpbin /symbols ssh.lib | 能找到 ssh_new |
| 最小程序 | 编译并运行第 4 章示例 | 输出 0.10.3 版本 |
最小程序能跑通之后,再把它接到真实业务代码里,先只调用ssh_new和ssh_free,确认静态链接没有问题,再逐步加ssh_options_set、ssh_connect。从那以后我每次拿到新的 libssh 静态库,都会强制走一遍“文件、机器类型、符号、最小程序”这四步,不省略任何一层;这套 ssh.lib 和头文件已经按 Release/x64 目录整理好,拿到后先按上面的顺序验一遍,比直接塞进工程里省时间。希望帮到你。
本文还有配套的精品资源,点击获取