Visual Studio 2017 / 2015 命令行编译 找不到 stdlib.h 的问题

上周折腾了下VS不能使用XP系统工具集(如:vc140_xp)编译的问题,原因是不知道为何以前安装的windows SDK 8 不完整,但是单独安装winsdk 8.0的话安装包又崩溃退出,包括用vs2015和vs2017修复都修复不成功。

所以最后是彻底卸载了vs2015、vs2017后依次再重装解决问题。
结果本周在mingw+msvc编译某项目时就报出了错误fatal error C1083: 无法打开包括文件: “stdio.h”: No such file or directory

但是单独使用VS2015或VS2017去编译时又可以编译成功,右键stdio.h打开头文件时定位到了路径"C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt\stdio.h",为什么IDE能够找到头文件而命令行不行呢?

接着VS2015 x86 本机工具命令提示符中执行set INCLUDE时发现包含了一系列头文件路径中有C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\ucrt但是没有10.0.10240.0,而10.0.15063.0下没有ucrt目录。

一开始觉得可能是10.0.15063.0的安装有问题,就打开vs2017的安装程序点修改,发现所有10.0.15063.0相关的安装选项都是勾上的。然后将其全部反勾选并且勾选上另一个win10 sdk 10.0.14393.0,然后安装后使用命令行成功编译,看INCLUDE也能看到ucrt目录变成了"C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0"下的了。

然后再修改安装包将所有10.0.15063.0相关的安装选项都勾上,再安装后用命令行发现INCLUDE也能看到ucrt目录变成了"C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0"下的了,然后也能编译成功了,自此命令行的问题解决了。

猜想:命令行查找win10 sdk的方式是找注册表中的最高版本号,并不验证用户是否安装完整的win10 sdk。

衍生问题:
我机器上还有两个不完整的win10 sdk,10.0.10150.0和10.0.10240.0,在vs里的项目属性中,如果选择平台工具集为Visual Studio 2017 - Windows XP (v141_xp),则Windows SDK版本默认变成了7.0,点击下拉后有可选项8.1,目标平台显示空白。
如果选择平台工具集为Visual Studio 2017 (v141),则Windows SDK版本可选项有10.0.14393.0、10.0.15063.0、8.1三个版本。如果选择8.1,则目标平台显示Windows;如果选择10.0.x.0,则目标平台显示Windows 10。

猜想:在IDE里可选的Windows SDK版本取自完整安装的sdk,如果平台工具集是v141_xp,为了支持XP,所以目标平台不做限定,且Windows SDK至多只能使用8.1。如果选择v141,则默认不支持xp,如果同时SDK版本选择了10.x,可能默认只支持win10(仅猜测,未验证)。

还有一个疑问,在我机器上同时存在4个版本的ucrt的情况下,IDE计算出来的包含目录是10.0.10240.0,即不是最小版本号的那个,也不是最大版本号的那个,这个就不清楚原因了,可能要看下MSBUILD的那些Props文件才可以得出答案了。

2017/10/11 补充:
“X:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat”的命令行如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Syntax:
vcvarsall.bat [arch] [platform_type] [winsdk_version] [-vcvars_ver=vc_version]
where :
[arch]: x86 | amd64 | x86_amd64 | x86_arm | x86_arm64 | amd64_x86 | amd64_arm | amd64_arm64
[platform_type]: {empty} | store | uwp
[winsdk_version] : full Windows 10 SDK number (e.g. 10.0.10240.0) or "8.1" to use the Windows 8.1 SDK.
[vc_version] : "14.0" for VC++ 2015 Compiler Toolset | {empty} for default VS 2017 VC++ compiler toolset

The store parameter sets environment variables to support Universal Windows Platform application
development and is an alias for 'uwp'.

For example:
vcvarsall.bat x86_amd64
vcvarsall.bat x86_amd64 10.0.10240.0
vcvarsall.bat x86_arm uwp 10.0.10240.0
vcvarsall.bat x86_arm onecore 10.0.10240.0 -vcvars_ver=14.0
vcvarsall.bat x64 8.1
vcvarsall.bat x64 store 8.1

看命令行中可以指定windows sdk的版本号。如果未指定,看bat的代码是引用 X:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat时不加-winsdk参数,然后在VsDevCmd.bat里转调X:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\vsdevcmd\core\winsdk.bat来确定windows sdk的版本号,在bat里有这么一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
:GetWin10SdkDirHelper

@REM Get Windows 10 SDK installed folder
for /F "tokens=1,2*" %%i in ('reg query "%1\Microsoft\Microsoft SDKs\Windows\v10.0" /v "InstallationFolder"') DO (
if "%%i"=="InstallationFolder" (
SET WindowsSdkDir=%%~k
)
)

@REM get windows 10 sdk version number
setlocal enableDelayedExpansion
if not "%WindowsSdkDir%"=="" for /f %%i IN ('dir "%WindowsSdkDir%include\" /b /ad-h /on') DO (
@REM Skip if Windows.h is not found in %%i\um. This would indicate that only the UCRT MSIs were
@REM installed for this Windows SDK version.
if EXIST "%WindowsSdkDir%include\%%i\um\Windows.h" (
set result=%%i
if "!result:~0,3!"=="10." (
set SDK=!result!
if "!result!"=="%VSCMD_ARG_WINSDK%" set findSDK=1
)
)
)

由上面代码中看出,bat里只检测windows 10安装目录里是否有某版本的include目录里有um\windows.h即拿来使用,所以才导致上面提到的引用了一个安装不完整的win10 sdk的问题。