
简介在Windows 64位环境下集成SSH2能力时C/C开发者常被libssh2的编译流程困扰。这份资源直接给出使用VS2017编译完成的64位libssh2库包内共115个文件含109个头文件、3个静态库、2个运行所需DLL及1个调用示例源文件覆盖SSH2核心API、OpenSSL加密依赖与基本使用演示整体仅2.14MB非常轻量。库已针对Release模式构建拿到后只需在VS2017中配置头文件目录与链接器输入即可接入项目省去手动配置CMake、OpenSSL及依赖环境的步骤。资源同时附带了libcrypto和libssl动态库能有效减少OpenSSL版本不匹配问题便于快速验证与二次开发。目前已有1507人学习下载适合需要快速落地libssh2功能的中初级C/C开发者参考使用。1. 为什么要自己编译libssh2而不是直接下载现成包先说个故事。我去年接手一个Windows平台的内部工具项目需要在C代码里通过SFTP上传文件到远程服务器第一个想到的方案就是libssh2——它是用C写的SSH2协议库轻量、跨平台、无运行时依赖在嵌入式领域和桌面工具里都有大量应用。它的定位和libcurl不同libcurl是通用网络传输库libssh2只聚焦SSH2协议本身但正因为专注它更纯粹集成起来也更可控。当时我的开发环境是VS2017目标平台是x64。按理说libssh2在GitHub上发布了预编译的二进制包直接下载用不就行了我一开始也这么想但实际用起来发现几个问题官方提供的预编译包版本往往滞后最新特性或者某些bugfix拿不到。我需要同时集成OpenSSL做加密后端预编译包未必和我的OpenSSL版本匹配导致符号冲突或者ABI不兼容。我想按自己的需要裁剪掉一些模块比如不用zlib压缩预编译包做不到。所以最稳妥的方案就是在本地用VS2017手动编译一套64位的libssh2。这个过程我在网上查过不少资料但很多教程要么太老要么步骤不够完整实际操作中会遇到各种莫名其妙的问题。这篇文章把我整个编译过程、踩过的坑、每一步的原理都梳理一遍希望帮你省掉这些时间。适合谁来参考如果你用的是VS2017或VS2015/2019目标平台是64位需要在自己的项目里集成libssh2做SFTP/SCP或者远程命令执行那这篇内容基本能覆盖你需要的全部信息。2. 编译前置条件工具链、CMake、OpenSSL和Perl2.1 VS2017的C工具链必须装全很多人的VS2017在安装的时候只勾选了“.NET桌面开发”或者“通用Windows平台开发”C相关的组件压根没装或者没装全。编译libssh2是纯C代码但CMake在检测编译器的过程中会检查C和C两套工具链。你需要在Visual Studio Installer里确认安装了以下组件“使用C的桌面开发”工作负载单个组件里的“Windows 10 SDK”版本不限但要匹配你系统“VC 2017版本15.9 v14.16最新v141工具集”每个版本的命名可能略有差异注意如果你系统里装了多个版本的VSCMake在生成项目文件的时候可能会选错编译器版本。后面我会讲怎么固定让CMake使用指定的VS版本。2.2 CMake版本不是越新越好但要够用libssh2官方建议的CMake最低版本是3.15左右。我用的是CMake 3.20.3。你可以在cmake.org下载Windows安装包安装时记得勾选“Add CMake to the system PATH for all users”这样命令行里能直接敲cmake命令。如果你不想装到系统里也可以只用CMake的GUI图形界面两个方式我都试过各有优劣。这篇以命令行方式为主因为它的可复现性更强——你写完一条命令以后换台机器能一模一样地执行。2.3 OpenSSL依赖静态库还是动态库libssh2的实际加密能力AES、RSA、SHA等依赖OpenSSL。编译libssh2时不强制依赖OpenSSL它可以只用内置的crypto backend但功能会受限很多场景下没法满足需求。我建议直接集成OpenSSL 1.1.1系列。为什么不推荐OpenSSL 3.x因为3.x授权协议改成了Apache License 2.0功能上是兼容的但在Windows上编译静态库时配置流程和依赖项有细微差别。对大多数项目来说1.1.1足够稳定毕竟是LTS版本技术支持到2023年9月。如果你要用3.x编译思路一样只是AES-NI这类优化需要额外处理一下。获取OpenSSL编译产物的方式有两种从slproweb.com下载Shining Light Productions的预编译Windows安装包。安装时它会提供完整的include头文件、lib库文件和dll文件。用Perl自己从源码编译OpenSSL。我建议用方式1——预编译包足够日常使用能省下大量时间。安装时注意选Win64 OpenSSL v1.1.1而不是Win32版本。它的安装目录默认在C:\OpenSSL-Win64里面会有include和lib两个文件夹lib文件夹里有libssl.lib和libcrypto.lib。提示安装OpenSSL预编译包时最后一步会问你要不要复制DLL到系统目录建议选“不复制”。后面在项目里部署DLL时放到exe同目录比放系统目录要干净得多升级和卸载都方便。2.4 Perl编译OpenSSL源码才需要如果你走“源码编译OpenSSL”这条路环境里必须有Perl推荐Strawberry Perl。这个工具是Windows上编译OpenSSL的前提条件因为OpenSSL的Configure脚本是用Perl写的。如果你直接用预编译OpenSSL包那Perl可以不用装。我这次为了省事用的是预编译包所以Perl这一步直接跳过了。2.5 编译环境变量确认在开始之前打开一个“x64 Native Tools Command Prompt for VS2017”开始菜单里Visual Studio 2017文件夹下能找到做一个检查cl如果输出版本信息说明C编译环境正常。再执行cmake --version确认CMake能正常调用。同时执行where cmake确认它在PATH里。3. 用CMake配置libssh2的完整流程3.1 获取libssh2源码从libssh2的GitHub仓库拉取源码。建议不要直接下载zip包而是用git clone方式这样之后你想切换分支或者更新版本都很方便git clone https://github.com/libssh2/libssh2.git cd libssh2注意当前master分支的版本可能需要更新的CMake配置如果你想要稳定版本可以切换到某个固定tag。我当时用的是libssh2-1.10.0这个taggit checkout libssh2-1.10.01.10.0版本对VS2017和CMake的兼容性都很好编译过程很顺利。3.2 创建构建目录libssh2源码目录下最好不要直接生成构建文件因为这样会污染源码树以后更新代码的时候很麻烦。规范做法是创建一个独立的build目录mkdir build-vs2017-x64 cd build-vs2017-x643.3 关键的CMake命令接下来这条命令是整个编译流程的核心我逐段解释一下cmake .. -G Visual Studio 15 2017 Win64 -DCMAKE_INSTALL_PREFIXC:/libssh2-install -DBUILD_SHARED_LIBSON -DBUILD_EXAMPLESOFF -DBUILD_TESTINGOFF -DCRYPTO_BACKENDOpenSSL -DOPENSSL_ROOT_DIRC:/OpenSSL-Win64参数拆解-G Visual Studio 15 2017 Win64指定生成器为VS2017的64位版本。这里有个细节如果用Visual Studio 15 2017不带Win64默认生成的是32位工程网上很多人在这里踩坑。VS2017对应的generator名称是Visual Studio 15 2017注意15是VS2017的内部版本号。-DCMAKE_INSTALL_PREFIXC:/libssh2-install指定install的安装目录。编译完成后运行cmake --install会把头文件、库文件、cmake配置文件统一放到这个目录方便项目引用。-DBUILD_SHARED_LIBSON构建动态库DLL。如果你想要静态库.lib设为OFF即可。这个根据自己的需求选。-DBUILD_EXAMPLESOFF不编译示例程序节约时间。-DBUILD_TESTINGOFF不生成测试目标。libssh2的测试用例依赖一些Python库和外部SSH服务本地跑起来很麻烦先关掉。-DCRYPTO_BACKENDOpenSSL指定加密后端为OpenSSL。-DOPENSSL_ROOT_DIRC:/OpenSSL-Win64告诉CMake哪里找OpenSSL的头文件和库。执行完后CMake会输出一堆检测信息最后出现“Generating done”字样说明配置成功当前目录下会生成libssh2.sln解决方案文件。3.3.1 关于BUILD_SHARED_LIBS动态库vs静态库到底该怎么选这是很多人犹豫的点。我的建议是如果你的项目本身是动态加载插件架构或者你不想让OpenSSL的符号泄漏出去污染其他DLL选静态库BUILD_SHARED_LIBSOFF。如果你的项目有多个模块都要用libssh2静态库会导致每个模块各有一份拷贝内存浪费不说调试的时候符号还会互相打架这时候选动态库更好。另外要注意选静态库时libssh2.lib和libcrypto.lib有顺序要求链接的时候libssh2必须在前、OpenSSL库在后否则会出现无法解析的外部符号。3.4 编译Debug和Release版本有区别CMake生成VS解决方案后在Visual Studio里打开或者直接MSBuild编译都可以。我习惯直接用命令行编译cmake --build . --config Release --parallel 8--config Release是关键参数因为VS多配置生成器默认会有Debug、Release、RelWithDebInfo、MinSizeRel四种配置不指定的话有些目标会全部编译一遍浪费时间。--parallel 8表示8线程并行编译。编译完成后在build-vs2017-x64/src/Release目录下应该能看到libssh2.dlllibssh2.libDebug版本的产物在src/Debug目录下文件名里会带一个d后缀libssh2d.dll吗实际上libssh2不会在文件名里加d它只是输出路径不同。但implicit linking时使用的导入库名是一样的所以Debug和Release工程的LIB文件名一样需要你在自己的VS工程里注意选择对应目录的lib。4. OpenSSL依赖处理的几种姿势4.1 路径处理最容易出问题我第一次配置的时候把OPENSSL_ROOT_DIR指到了C:\OpenSSL-Win64这个变量里包含了完整路径。但有个小坑如果你的路径中间有空格比如C:\Program Files\OpenSSLCMake有时候解析会出问题宏定义里会出现路径截断的奇怪现象。最省心的做法是把OpenSSL放到一个没有空格的路径下比如C:\OpenSSL-Win64。因为CMake在生成项目文件时会把路径引用写到vcxproj文件的AdditionalIncludeDirectories和AdditionalLibraryDirectories里。有空格时会自动加引号但个别老版本CMake处理得并不好所以我们从源头避免它。4.2 检查CMake是否找到OpenSSL执行CMake配置时注意输出里有没有这两行Found OpenSSL: C:/OpenSSL-Win64/lib/libcrypto.lib (found version 1.1.1w)如果出现Could NOT find OpenSSL那就说明路径配错了。可以用这个命令查看CMake缓存里OpenSSL相关的变量cmake -LA . | findstr OPENSSL正常情况会输出OPENSSL_CRYPTO_LIBRARY:FILEPATHC:/OpenSSL-Win64/lib/libcrypto.lib OPENSSL_INCLUDE_DIR:PATHC:/OpenSSL-Win64/include OPENSSL_SSL_LIBRARY:FILEPATHC:/OpenSSL-Win64/lib/libssl.lib如果这些变量还是NOTFOUND多半是路径写错了或者32位/64位混用了。4.3 为什么不建议同时设置OPENSSL_INCLUDE_DIR和OPENSSL_ROOT_DIR这两个变量一个指定头文件目录一个指定库目录。如果同时设置但指向了两个不同版本的OpenSSL目录编译的时候头文件用的是A版本、链接时库用的B版本轻则警告重则运行时崩溃。我建议只设置OPENSSL_ROOT_DIR让CMake的FindOpenSSL模块自己推断include和lib的位置。5. 集成到自己的VS2017项目5.1 在工程属性里配置头文件和库编译好libssh2之后在VS2017里新建一个自己的项目然后在项目属性页里设置配置C/C - 常规 - 附加包含目录添加C:\libssh2-install\includeC:\OpenSSL-Win64\include配置链接器 - 常规 - 附加库目录添加C:\libssh2-install\libC:\OpenSSL-Win64\lib配置链接器 - 输入 - 附加依赖项添加libssh2.lib如果用的是静态库而不是动态库还需要额外链接OpenSSL的库libssl.liblibcrypto.lib5.2 运行时的DLL部署动态库的方式编译时运行你的exe之前需要把三个DLL放到exe同目录或者放到系统PATH里libssh2.dlllibssl-1_1-x64.dlllibcrypto-1_1-x64.dll我一般的习惯是写一个部署脚本用xcopy把这三个DLL拷贝到输出目录xcopy /Y C:\libssh2-install\bin\libssh2.dll $(OutDir) xcopy /Y C:\OpenSSL-Win64\bin\libssl-1_1-x64.dll $(OutDir) xcopy /Y C:\OpenSSL-Win64\bin\libcrypto-1_1-x64.dll $(OutDir)放到预构建事件里每次编译自动执行省得手动去翻目录。5.3 最小测试代码配置完之后写一个最简单的调用验证一下#include libssh2.h #include iostream int main() { libssh2_init(0); std::cout libssh2 version: LIBSSH2_VERSION std::endl; libssh2_exit(); return 0; }如果编译链接通过运行输出libssh2的版本号说明整个编译链路已经通了。6. 编译过程中可能遇到的坑及排查方法6.1 错误无法打开文件libssh2.lib这个错误发生在链接阶段。基本原因只有一个——链接器找不到导入库。检查三点附加库目录路径是否正确用绝对路径别用相对路径。lib文件名是否匹配。有的教程编译出来是libssh2.lib但如果你设置了CMAKE_DEBUG_POSTFIXDebug版本可能叫libssh2d.lib。编译架构是否一致。你的项目平台是x64链接的lib也必须是x64编译出来的。用32位的lib去链接x64工程会出现LNK1112错误。6.2 错误LNK2038 不匹配这是VS2017经常出现的经典错误LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MD_DynamicRelease”不匹配值“MT_StaticRelease”意思是说你的主工程用了动态运行时/MD但libssh2是用静态运行时/MT编译的两者不匹配。要解决需要在编译libssh2时保持和主工程一致的运行时库设置。有两种改法改libssh2的CMake配置在CMakeLists里设置CMAKE_C_FLAGS_RELEASE为/MD。或者更推荐的做法让你主工程统一使用和libssh2一样的运行时。比如libssh2默认是/MD那你的主工程就把“代码生成 - 运行库”改为“多线程DLL(/MD)”。这个不匹配的原因是MSVC会把运行时类型编码到lib的符号里链接的时候做一致性检查。做跨库集成时这是最常见的问题记得先检查运行库设置。6.3 错误无法解析的外部符号__imp_*相关形如unresolved external symbol __imp_libssh2_session_init referenced in function main说明你在链接一个用__declspec(dllimport)声明的函数但对应导入库没有被正确链接。一般是因为只加了include路径而忘了加lib目录或者lib目录背景选的还是Debug模式但在Release下编译。还有一种可能是你链接的不是导入库而是静态库但代码里LIBSSH2_API的声明被定义成了__declspec(dllimport)——这可以在预处理器里加LIBSSH2_API来避免。6.4 编译警告C4996有时会看到warning C4996: strcpy: This function or variable may be unsafe.这是微软对非安全版本C库函数的提示不是错误。可以在预处理定义加_CRT_SECURE_NO_WARNINGS来消除。因为libssh2源码原本是跨平台的后端的加密逻辑在Windows上触发这类警告很正常不影响使用。6.5 运行时找不到DLL编译链接都通过了但运行时弹窗提示找不到libssh2.dll。解决办法我之前已经说过把三个DLL放exe同目录。不过我注意到一个现象如果把DLL放到系统System32目录有时候会加载到旧版本的同名DLL比如其他软件塞进去的导致接口不匹配崩溃。所以尽量坚持DLL和exe同目录原则不要往系统目录放。7. 关于静态编译和动态编译的实际选择建议先说结论可分发的小工具强烈建议用静态编译。原因很简单——动态编译需要带三个DLL不管做安装包还是绿色版文件都会多出好几MB而且OpenSSL的DLL在不同机器上可能出现各种兼容性问题。尤其是目标机器上如果装了其他用OpenSSL的软件DLL替换升级时很可能崩掉。静态编译要注意的就是上面提到的运行库一致性问题。把BUILD_SHARED_LIBS设为OFF同时编译整个依赖链时保持所有库的运行库设置一致。另外静态编译的libssh2连接OpenSSL时需要在链接参数里保证库的顺序libssh2.lib libssl.lib libcrypto.lib如果还依赖了zlib加上zlib.lib。如果编译顺序反了你可能会遇到很多奇怪的“无法解析的外部符号”其实不是真的缺符号而是链接器按照从左到右的顺序解析库符号时前面的库引用后面的库符号没法回填。MSVC不像GCC那样会循环解析。所以库顺序是静态链接时最容易忽视的细节。7.1 静态库的调试符号问题如果你选择了静态编译并准备在开发阶段调试libssh2内部的调用流程记得在CMake配置时把CMAKE_BUILD_TYPE设为对应配置并打开调试信息。使用VS多配置生成器时Debug配置默认会带上/Zi这没问题。在你的主项目里把附加依赖库切换为libssh2d.lib如果设置了postfix就能断点调试到libssh2源码内部。8. 我编译过程中翻过的最大的车最后分享一个我真实遇到的坑希望你别再踩。我当时手头有个老项目依赖了32位的第三方库主工程一直是Win32平台。后来要出一个x64版本时我直接把整个解决方案切到了x64编译时发现libssh2链接怎么都过不去。排查半天发现我的OPENSSL_ROOT_DIR还指向C:\OpenSSL-Win64没错但这个目录装的是32位还是64位我自己都没确认。用dumpbin /headers libcrypto.lib一查文件头里写着machine (x86)。也就是说我用32位的OpenSSL去配64位的libssh2CMake竟然没报错编译也过了但链接时符号对齐全乱了。所以建议你拿到OpenSSL预编译包后先确认一下它的架构dumpbin /headers C:\OpenSSL-Win64\lib\libcrypto.lib | findstr machine输出x64才是对的。还有一次我换了一台新机器重新编译CMake配置完成后编译报错提示找不到win32相关头文件。原因是新机器上Windows SDK没装。VS2017在安装时默认会装最新版WDK或者SDK但如果选了自定义安装很容易漏掉。重新运行VS安装器补一个“Windows 10 SDK”组件就好。9. 给Windows下习惯用命令行人的额外建议在Windows环境下用CMake命令行编译建议先把VS的环境变量导入当前控制台再执行。也就是打开“x64 Native Tools Command Prompt for VS2017”而不是用普通的PowerShell。因为普通PowerShell里没有cl.exe的环境变量CMake虽然能自己找到编译器但有时候还是会因为找不到rc.exe资源编译器报错。如果你实在只有PowerShell可以用vcvars64.bat手动导入环境cmd /k C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat注意路径里的版本号和VS版本Community/Professional/Enterprise需要按你自己的安装情况改。10. 最后的最后一个小技巧编译完成后C:\libssh2-install目录里会有一个libssh2-config.cmake文件。下次你在自己的CMake工程里可以直接用find_package(libssh2 REQUIRED)来引用它不用手动添加include和lib路径set(libssh2_DIR C:/libssh2-install/lib/cmake/libssh2) find_package(libssh2 REQUIRED) target_link_libraries(my_target PRIVATE libssh2::libssh2)这会帮你省掉一堆工程配置的琐碎步骤。如果你只在VS里做小工程用我之前说的属性页手工配置也完全够用。我在实际编译libssh2的过程中第一次配置花了整整一个下午大部分时间都耗在OpenSSL路径和运行库不匹配这类问题上。但搞定一次之后后面换版本、换机器、换依赖项都变得很轻松基本上十分钟内能跑通全流程。希望这篇文章能帮你把这个过程压缩到一杯咖啡的时间。本文还有配套的精品资源点击获取