// 指定为 Windows XP
#define _WIN32_WINNT 0x0501
#define WINVER 0x0501
#include "BaseInclude.h"
#include "CDownloadFile.h"
#include "CGetOSVersion.h"
#include "CRegistryVisit.h"
using namespace std;
// 版本常量,必须与 Shared/NetVersions.cs 保持一致
#define NET6_VERSION "6.0.36"
#define NET8_VERSION "8.0.33"
#define NET10_VERSION "10.0.14"
#define NET11_VERSION "11.0.0"
static bool _silent = false;
static bool _force = false;
static bool _dryRun = false;
static string _server = "http://x.newlifex.com";
static string _serverPath = "/dotnet/";
static OSVersionInfo _os;
// 前置声明:这两个函数在很多位置被调用,定义见文件后部
static bool InstallPackage(const string& fileName, const string& args);
static string ExplainExitCode(DWORD code);
/// <summary>获取程序所在目录</summary>
static string GetExeDir()
{
char path[MAX_PATH] = { 0 };
GetModuleFileNameA(NULL, path, MAX_PATH);
string file = path;
auto p = file.find_last_of('\\');
return p == string::npos ? "." : file.substr(0, p);
}
/// <summary>解析服务器地址,拆分为服务器与基准路径</summary>
static void ParseServerUrl(const string& url)
{
auto line = Trim(url);
if (line.empty()) return;
auto pos = line.find("://");
if (pos == string::npos)
{
_server = line;
return;
}
auto slash = line.find('/', pos + 3);
if (slash == string::npos)
{
_server = line;
return;
}
_server = line.substr(0, slash);
auto path = line.substr(slash);
if (path.empty()) return;
if (path[path.size() - 1] != '/') path += '/';
_serverPath = path;
}
/// <summary>读取本目录 server.txt 中的服务器地址</summary>
static void LoadServerConfig()
{
auto file = GetExeDir() + "\\server.txt";
ifstream infile(file.c_str());
if (!infile) return;
string line;
if (!getline(infile, line)) return;
ParseServerUrl(line);
}
/// <summary>下载文件,已存在则直接返回本地路径</summary>
static string DownloadFile(const string& fileName, const string& remotePath)
{
auto file = GetExeDir() + "\\" + fileName;
// 已存在且非空则直接使用,空文件(可能是中断留下的)重新下载
WIN32_FILE_ATTRIBUTE_DATA fad;
if (GetFileAttributesExA(file.c_str(), GetFileExInfoStandard, &fad) &&
(fad.nFileSizeHigh > 0 || fad.nFileSizeLow > 0)) return file;
cout << "下载:" << _server << remotePath << endl;
cout << "保存:" << file << endl;
CDownloadFile cDownFile;
for (auto i = 0; i < 15; i++)
{
if (cDownFile.Download(file, _server, remotePath)) return file;
// 沉默1秒后重试
Sleep(1000);
}
return "";
}
/// <summary>执行进程并等待退出,必要时通过 runas 触发 UAC</summary>
static bool RunProcess(const string& file, const string& args)
{
if (file.empty()) return false;
cout << "执行:" << file << " " << args << endl;
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
auto cmdLine = "\"" + file + "\"";
if (!args.empty()) cmdLine += " " + args;
auto ok = CreateProcessA(NULL, &cmdLine[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
auto err = GetLastError();
if (!ok && err == ERROR_ELEVATION_REQUIRED)
{
// 安装程序要求管理员权限时,通过 runas 弹出UAC确认
SHELLEXECUTEINFOA sei;
ZeroMemory(&sei, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = "runas";
sei.lpFile = file.c_str();
sei.lpParameters = args.empty() ? NULL : args.c_str();
sei.nShow = SW_SHOWNORMAL;
if (!ShellExecuteExA(&sei))
{
cout << "启动安装程序失败:" << GetLastError() << endl;
return false;
}
pi.hProcess = sei.hProcess;
}
else if (!ok)
{
cout << "启动安装程序失败:" << err << endl;
return false;
}
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD code = 0;
GetExitCodeProcess(pi.hProcess, &code);
CloseHandle(pi.hProcess);
if (pi.hThread != NULL) CloseHandle(pi.hThread);
// 3010/1641 表示安装成功但需要重启
if (code == 0 || code == 3010 || code == 1641)
{
cout << "安装完成!" << endl;
return true;
}
cout << "安装失败!退出码:" << code << endl;
auto tip = ExplainExitCode(code);
if (!tip.empty()) cout << "原因:" << tip << endl;
return false;
}
/// <summary>把安装器退出码翻译成可执行的建议</summary>
static string ExplainExitCode(DWORD code)
{
switch (code)
{
case 5: return "拒绝访问,需要以管理员身份运行";
case 1603: return "致命错误:XP/2003 常见原因是缺少 WIC(Windows Imaging Component),也可能是权限不足、源文件损坏或已存在更高版本";
case 5100: return "系统中已存在更高版本的 .NET";
case 1638: return "已安装同版本或更高版本";
case 1618: return "另一个安装正在进行,请等待其完成后重试";
default: return "";
}
}
/// <summary>读取 PE 文件的目标架构(IMAGE_FILE_MACHINE_*),不是 PE 文件时返回 0</summary>
static DWORD GetPeMachine(const string& file)
{
auto h = CreateFileA(file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) return 0;
DWORD machine = 0;
BYTE head[1024];
DWORD read = 0;
if (ReadFile(h, head, sizeof(head), &read, NULL) && read >= 0x40 && head[0] == 'M' && head[1] == 'Z')
{
auto pe = *(DWORD*)(head + 0x3C);
if (pe > 0 && pe + 6 <= read && head[pe] == 'P' && head[pe + 1] == 'E') machine = *(WORD*)(head + pe + 4);
}
CloseHandle(h);
return machine;
}
/// <summary>校验安装包架构与本机是否匹配</summary>
static bool CheckArch(const string& file)
{
auto machine = GetPeMachine(file);
if (machine == 0) return true;
auto is64 = machine == 0x8664 || machine == 0xAA64 || machine == 0x0200;
auto name = machine == 0x014C ? "x86" : machine == 0x8664 ? "x64" : machine == 0xAA64 ? "arm64" : "ia64";
// 架构不符时尽早报错,否则只会得到“不是有效的 Win32 应用程序”这类无效信息
if (is64 && !_os.Is64Bit)
{
cout << "跳过 " << name << " 版安装包,当前系统只有 32 位:" << file << endl;
return false;
}
return true;
}
/// <summary>XP/2003 安装 .NET4.0 的前置:Windows Imaging Component</summary>
static bool EnsureWic()
{
char win[MAX_PATH] = { 0 };
if (GetWindowsDirectoryA(win, MAX_PATH) > 0)
{
auto dll = string(win) + "\\System32\\WindowsCodecs.dll";
if (GetFileAttributesA(dll.c_str()) != INVALID_FILE_ATTRIBUTES) return true;
}
auto file = _os.Is64Bit ? "wic_x64.exe" : "wic_x86.exe";
cout << "缺少 WIC(Windows Imaging Component),先安装 " << file << endl;
auto args = _silent ? string("/quiet /norestart") : string();
if (InstallPackage(file, args)) return true;
cout << "WIC 未能自动安装,.NET4.0 会以 1603 失败;请先手动安装 WIC 后重试" << endl;
return false;
}
/// <summary>下载并安装一个安装包</summary>
static bool InstallPackage(const string& fileName, const string& args)
{
auto file = DownloadFile(fileName, _serverPath + fileName);
if (file.empty())
{
cout << "下载失败!" << endl;
return false;
}
if (!CheckArch(file)) return false;
return RunProcess(file, args);
}
/// <summary>判断核心运行时是否支持当前操作系统</summary>
static bool IsSupported(const string& version)
{
int major = 0;
sscanf_s(version.c_str(), "%d", &major);
// Win10 1607 起支持所有核心运行时
if (_os.Major >= 10) return _os.Build >= 14393;
if (major == 6)
{
if (_os.Major != 6) return false;
// .NET6 支持 Win7 SP1、Win8.1 与 Server 2012+,Win8 客户端不支持
if (_os.Minor == 1) return _os.Build >= 7601;
if (_os.Minor == 2) return _os.IsServer;
return true;
}
// .NET8/10/11 仅支持 Server 2012/2012 R2 及以上
if (major == 8 || major == 10 || major == 11) return _os.IsServer && _os.Major == 6 && _os.Minor >= 2;
return false;
}
/// <summary>按操作系统获取自动安装的运行时列表</summary>
static vector<string> GetAutoTargets()
{
vector<string> list;
// Windows 2000/XP/2003 只支持 .NET Framework 2.0/4.0
if (_os.Major <= 5)
{
list.push_back("net20");
list.push_back("net40");
return list;
}
// Vista/Server 2008,4.5.2 是最后支持的版本
if (_os.Major == 6 && _os.Minor == 0)
{
list.push_back("net45");
return list;
}
// Win7/Server 2008 R2,只有 SP1 才支持 .NET Core
if (_os.Major == 6 && _os.Minor == 1)
{
list.push_back("net48");
if (_os.Build >= 7601) list.push_back("net6");
return list;
}
// Win8/8.1/Server 2012/2012 R2
if (_os.Major == 6)
{
list.push_back("net48");
// Win8(6.2)客户端不受 .NET Core 支持,Server 2012 受支持
if (_os.Minor >= 3 || _os.IsServer) list.push_back("net6");
return list;
}
// Win10 1607+/Win11/Server 2016+
if (_os.Major >= 10 && _os.Build >= 14393)
{
list.push_back("net48");
list.push_back("net10");
return list;
}
list.push_back("net48");
return list;
}
/// <summary>安装 .NET Framework 2.0</summary>
static bool InstallNet20(CRegistryVisit& reg)
{
if (reg.CheckIsInstallNet(_T("2.0")))
{
cout << "已安装 .NET2.0" << endl;
return true;
}
// .NET3.5 包含 2.0 运行时
if (_os.Major >= 6 && reg.CheckIsInstallNet(_T("3.5")))
{
cout << "已通过 .NET3.5 提供 .NET2.0" << endl;
return true;
}
if (_os.Major >= 6 && _os.Minor >= 2)
{
cout << "Windows 8 及以上请在“启用或关闭 Windows 功能”中安装 .NET Framework 3.5" << endl;
return false;
}
if (_dryRun)
{
cout << "将安装 .NET2.0" << endl;
return true;
}
// .NET2.0 SP2 要求 XP SP2 / 2003 SP1 及以上;低于该版本时安装器会长时间无响应,先给出结论
if (_os.Major == 5)
{
auto need = _os.Minor == 1 ? 2 : 1;
if (_os.ServicePack < need && !_force)
{
cout << "当前系统 Service Pack " << _os.ServicePack << " 低于 .NET2.0 要求的 SP" << need << ",请先安装系统补丁包,或加 -force 强制尝试" << endl;
return false;
}
}
// 64 位系统装 x64 包:x86 包会挂起在会话 0(实测 2003 R2 x64)
auto file = _os.Is64Bit ? "NetFx20SP2_x64.exe" : "NetFx20SP2_x86.exe";
return InstallPackage(file, _silent ? "/quiet /norestart" : "");
}
/// <summary>安装 .NET Framework 4.0</summary>
static bool InstallNet40(CRegistryVisit& reg)
{
if (reg.CheckIsInstallNet(_T("4.0")))
{
cout << "已安装 .NET4.0" << endl;
return true;
}
if (_dryRun)
{
cout << "将安装 .NET4.0" << endl;
return true;
}
if (_os.Major <= 5)
{
// .NET4.0 官方要求 XP SP3 / 2003 SP2,低版本安装器会以 1603 失败
auto need = _os.Minor == 1 ? 3 : 2;
if (_os.ServicePack < need && !_force) cout << "注意:.NET4.0 要求 SP" << need << ",当前为 SP" << _os.ServicePack << ",安装可能失败" << endl;
// XP/2003 上 .NET4.0 依赖 WIC,缺了必然 1603
EnsureWic();
}
return InstallPackage("dotNetFx40_Full_x86_x64.exe", _silent ? "/passive /norestart" : "");
}
/// <summary>安装 .NET Framework 4.5.2</summary>
static bool InstallNet45(CRegistryVisit& reg)
{
if (reg.CheckIsInstallNet(_T("4.5")))
{
cout << "已安装 .NET4.5" << endl;
return true;
}
if (_os.Major <= 5)
{
cout << "Windows XP 最高支持 .NET4.0" << endl;
return false;
}
if (_dryRun)
{
cout << "将安装 .NET4.5.2" << endl;
return true;
}
// 语言包只需安装一个,中文优先,失败再回退英文版
auto args = _silent ? string("/passive /norestart") : string();
if (InstallPackage("NDP452-KB2901907-x86-x64-AllOS-CHS.exe", args)) return true;
return InstallPackage("NDP452-KB2901907-x86-x64-AllOS-ENU.exe", args);
}
/// <summary>安装 .NET Framework 4.8/4.8.1</summary>
static bool InstallNet48(CRegistryVisit& reg)
{
// win10/win11 中安装 .NET4.8.1
auto isWin10 = _os.Major >= 10;
auto target = isWin10 ? _T("4.8.1") : _T("4.8");
if (reg.CheckIsInstallNet(target))
{
cout << "已安装 .NET" << (isWin10 ? "4.8.1" : "4.8") << endl;
return true;
}
if (_os.Major <= 5 || (_os.Major == 6 && _os.Minor == 0))
{
cout << "当前系统最高支持 .NET4.5.2,请使用 net45" << endl;
return false;
}
if (_dryRun)
{
cout << "将安装 .NET" << (isWin10 ? "4.8.1" : "4.8") << endl;
return true;
}
auto args = _silent ? string("/passive /norestart /showfinalerror") : string();
if (isWin10)
{
if (InstallPackage("ndp481-x86-x64-allos-chs.exe", args)) return true;
return InstallPackage("ndp481-x86-x64-allos-enu.exe", args);
}
if (InstallPackage("ndp48-x86-x64-allos-chs.exe", args)) return true;
return InstallPackage("ndp48-x86-x64-allos-enu.exe", args);
}
/// <summary>安装 .NET 核心运行时</summary>
static bool InstallNetCore(CRegistryVisit& reg, const string& version)
{
int major = 0;
sscanf_s(version.c_str(), "%d", &major);
auto installed = reg.GetNetCoreVersion(major);
if (!installed.empty() && !_force && !IsNewerVersion(version, installed))
{
cout << "已安装 .NET" << version << "(当前 " << installed << ")" << endl;
return true;
}
if (!IsSupported(version))
{
cout << "当前系统不支持 .NET" << version << ",如需强制安装请加 -force" << endl;
if (!_force) return false;
}
if (_dryRun)
{
cout << "将安装 .NET" << version << endl;
return true;
}
auto arch = _os.Is64Bit ? "x64" : "x86";
// win7需要vc运行时与UCRT补丁,失败不阻塞后续安装
if (_os.Major == 6 && _os.Minor == 1)
{
auto kb = string("Windows6.1-KB3063858-") + arch + ".msu";
auto kbFile = DownloadFile(kb, _serverPath + "win7/" + kb);
if (!kbFile.empty()) RunProcess("wusa.exe", "\"" + kbFile + "\" /quiet /norestart");
auto vc = string("VC_redist.") + arch + ".exe";
auto vcFile = DownloadFile(vc, _serverPath + "vc/" + vc);
if (!vcFile.empty()) RunProcess(vcFile, "/install /quiet /norestart");
}
auto args = _silent ? string("/install /quiet /norestart") : string();
// 服务器安装托管捆绑包(含运行时与ASP.NET Core),客户端安装桌面运行时
if (_os.IsServer)
{
auto file = "dotnet-hosting-" + version + "-win.exe";
return InstallPackage(file, args);
}
else
{
auto file = "windowsdesktop-runtime-" + version + "-win-" + arch + ".exe";
return InstallPackage(file, args);
}
}
/// <summary>获取 .NET Framework 版本名称</summary>
static string GetNetFxName(DWORD release)
{
if (release >= 533320) return "4.8.1";
if (release >= 528040) return "4.8";
if (release >= 461808) return "4.7.2";
if (release >= 460798) return "4.7";
if (release >= 394254) return "4.6.2";
if (release >= 393295) return "4.6";
if (release >= 379893) return "4.5.2";
if (release >= 378389) return "4.5";
return "";
}
/// <summary>打印已安装版本</summary>
static void PrintInstalled(CRegistryVisit& reg)
{
cout << endl;
cout << "已安装版本:" << endl;
const char* versions[] = { "2.0", "3.5" };
for (auto item : versions)
{
if (reg.CheckIsInstallNet(CString(item))) cout << "v" << item << endl;
}
auto release = reg.GetNetFxRelease();
auto fx = GetNetFxName(release);
if (!fx.empty())
cout << "v" << fx << endl;
else if (reg.CheckIsInstallNet(_T("4.0")))
cout << "v4.0" << endl;
const int majors[] = { 6, 8, 10, 11 };
for (auto item : majors)
{
auto ver = reg.GetNetCoreVersion(item);
if (!ver.empty()) cout << "v" << ver << endl;
}
}
/// <summary>打印用法</summary>
static void PrintUsage()
{
cout << "用法:clover.exe [命令] [选项]" << endl;
cout << " auto 按操作系统自动选择(默认)" << endl;
cout << " net20|net40|net45|net48|net6|net8|net10|net11 安装指定运行时" << endl;
cout << " all 安装当前系统支持的全部运行时" << endl;
cout << " list 只列出已安装版本与自动安装计划" << endl;
cout << "选项:" << endl;
cout << " -silent 静默安装" << endl;
cout << " -force 已安装也强制安装,并忽略系统支持性检查" << endl;
cout << " -dryrun 只打印将要执行的操作,不下载不安装" << endl;
cout << " -server <url> 指定下载服务器,如 http://x.newlifex.com" << endl;
cout << "本地 server.txt 文件可设置下载服务器地址。" << endl;
}
/// <summary>解析命令行参数</summary>
static bool ParseArgs(int argc, char* argv[], string& cmd)
{
cmd = "";
for (auto i = 1; i < argc; i++)
{
string arg = argv[i];
if (arg == "-silent" || arg == "--silent")
_silent = true;
else if (arg == "-force" || arg == "--force")
_force = true;
else if (arg == "-dryrun" || arg == "--dryrun" || arg == "-dry-run")
_dryRun = true;
else if (arg == "-help" || arg == "--help" || arg == "-?" || arg == "/?")
cmd = "help";
else if ((arg == "-server" || arg == "--server") && i + 1 < argc)
ParseServerUrl(argv[++i]);
else if (!arg.empty() && arg[0] == '-')
{
cout << "无法识别的参数:" << arg << endl;
return false;
}
else if (cmd.empty())
cmd = arg;
else
{
cout << "多余的参数:" << arg << endl;
return false;
}
}
return true;
}
/// <summary>安装一个运行时</summary>
static bool InstallOne(const string& name, CRegistryVisit& reg)
{
if (name == "net20" || name == "net2") return InstallNet20(reg);
if (name == "net40" || name == "net4") return InstallNet40(reg);
if (name == "net45") return InstallNet45(reg);
if (name == "net48") return InstallNet48(reg);
if (name == "net6") return InstallNetCore(reg, NET6_VERSION);
if (name == "net8") return InstallNetCore(reg, NET8_VERSION);
if (name == "net10") return InstallNetCore(reg, NET10_VERSION);
if (name == "net11") return InstallNetCore(reg, NET11_VERSION);
cout << "无法识别的运行时:" << name << endl;
return false;
}
int main(int argc, char* argv[])
{
//检测OS版本状态
CGetOSVersion cOSVer;
_os = cOSVer.GetVersion();
LoadServerConfig();
string cmd;
if (!ParseArgs(argc, argv, cmd))
{
PrintUsage();
return 2;
}
cout << "幸运草 Clover v1.2 NewLife" << endl;
cout << "检测并安装主流.NET运行时。" << endl;
cout << "操作系统: " << _os.Desc << (_os.Is64Bit ? "(64位)" : "(32位)") << endl;
if (cmd == "help")
{
PrintUsage();
return 0;
}
CRegistryVisit reg;
PrintInstalled(reg);
vector<string> targets;
if (cmd.empty() || cmd == "auto" || cmd == "list")
targets = GetAutoTargets();
else if (cmd == "all")
{
const char* all[] = { "net20", "net40", "net45", "net48", "net6", "net8", "net10", "net11" };
for (auto item : all) targets.push_back(item);
}
else
{
if (!(cmd == "net20" || cmd == "net2" || cmd == "net40" || cmd == "net4" || cmd == "net45" || cmd == "net48" ||
cmd == "net6" || cmd == "net8" || cmd == "net10" || cmd == "net11"))
{
cout << "无法识别的命令:" << cmd << endl;
PrintUsage();
return 2;
}
targets.push_back(cmd);
}
if (cmd == "list")
{
cout << endl;
cout << "自动安装计划:" << endl;
for (auto item : targets) cout << " " << item << endl;
return 0;
}
cout << endl;
cout << "目标运行时:" << endl;
for (auto item : targets) cout << " " << item << endl;
auto failures = 0;
for (auto item : targets)
{
cout << endl;
cout << "===== " << item << " =====" << endl;
if (!InstallOne(item, reg)) failures++;
}
cout << endl;
if (failures == 0)
{
cout << "全部完成!" << endl;
return 0;
}
cout << failures << " 个组件安装失败!" << endl;
return 1;
}
|