Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow --releasify to specify the required .NET framework version #940

Merged
merged 5 commits into from
May 1, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 92 additions & 20 deletions src/Setup/FxHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
// http://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx#net_b
static const wchar_t* ndpPath = L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full";
static const int fx45ReleaseVersion = 378389;
static const int fx451ReleaseVersion = 378675; //Minimum version for .NET 4.5.1
static const int fx452ReleaseVersion = 379893;
static const int fx46ReleaseVersion = 393295; //Windows 10 version, other systems are higher
static const int fx461ReleaseVersion = 394254; // Minimum version for .NET 4.6.1
static const int fx462ReleaseVersion = 394802; // Minimum version for .NET 4.6.2

// According to https://msdn.microsoft.com/en-us/library/8z6watww%28v=vs.110%29.aspx,
// to install .NET 4.5 we must be Vista SP2+, Windows 7 SP1+, or later.
Expand All @@ -14,7 +19,21 @@ bool CFxHelper::CanInstallDotNet4_5()
return IsWindowsVistaOrGreater();
}

bool CFxHelper::IsDotNet45OrHigherInstalled()
NetVersion CFxHelper::GetRequiredDotNetVersion()
{
wchar_t* test = (wchar_t*)LoadResource(NULL, FindResource(NULL, (LPCWSTR)IDR_FX_VERSION_FLAG, L"FLAGS"));
CString resourceFlag(test);
if (resourceFlag.Compare(L"net451") == 0) return NetVersion::net451;
if (resourceFlag.Compare(L"net452") == 0) return NetVersion::net452;
if (resourceFlag.Compare(L"net46") == 0) return NetVersion::net46;
if (resourceFlag.Compare(L"net461") == 0) return NetVersion::net461;
if (resourceFlag.Compare(L"net462") == 0) return NetVersion::net462;

//Default to standard net45
return NetVersion::net45;
}

bool CFxHelper::IsDotNetInstalled(NetVersion required)
{
ATL::CRegKey key;

Expand All @@ -24,13 +43,32 @@ bool CFxHelper::IsDotNet45OrHigherInstalled()

DWORD dwReleaseInfo = 0;
if (key.QueryDWORDValue(L"Release", dwReleaseInfo) != ERROR_SUCCESS ||
dwReleaseInfo < fx45ReleaseVersion) {
dwReleaseInfo < GetDotNetVersionReleaseNumber(required)) {
return false;
}

return true;
}

int CFxHelper::GetDotNetVersionReleaseNumber(NetVersion version)
{
switch (version) {
case NetVersion::net451:
return fx451ReleaseVersion;
case NetVersion::net452:
return fx452ReleaseVersion;
case NetVersion::net46:
return fx46ReleaseVersion;
case NetVersion::net461:
return fx461ReleaseVersion;
case NetVersion::net462:
return fx462ReleaseVersion;
case NetVersion::net45:
default:
return fx45ReleaseVersion;
}
}

class ATL_NO_VTABLE CDownloadProgressCallback :
public CComObjectRoot,
public IBindStatusCallback
Expand All @@ -43,7 +81,7 @@ class ATL_NO_VTABLE CDownloadProgressCallback :
DECLARE_NOT_AGGREGATABLE(CDownloadProgressCallback)

BEGIN_COM_MAP(CDownloadProgressCallback)
COM_INTERFACE_ENTRY(IBindStatusCallback)
COM_INTERFACE_ENTRY(IBindStatusCallback)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()
Expand Down Expand Up @@ -84,24 +122,22 @@ class ATL_NO_VTABLE CDownloadProgressCallback :
CComPtr<IProgressDialog> m_spProgressDialog;
};

HRESULT CFxHelper::InstallDotNetFramework(bool isQuiet)
HRESULT CFxHelper::InstallDotNetFramework(NetVersion version, bool isQuiet)
{
if (!isQuiet) {
CTaskDialog dlg;
TASKDIALOG_BUTTON buttons [] = {
TASKDIALOG_BUTTON buttons[] = {
{ 1, L"Install", },
{ 2, L"Cancel", },
};


dlg.SetButtons(buttons, 2);
dlg.SetMainInstructionText(L"Install .NET 4.5");
dlg.SetContentText(L"This application requires the .NET Framework 4.5. Click the Install button to get started.");
dlg.SetMainInstructionText(GetInstallerMainInstructionForVersion(version));
dlg.SetContentText(GetInstallerContentForVersion(version));
dlg.SetMainIcon(TD_INFORMATION_ICON);

dlg.SetExpandedInformationText(
L"This application requires .NET Framework 4.5 or above. Clicking "
L"the Install button will download the latest version of this operating "
L"system component from Microsoft and install it on your PC.");
dlg.SetExpandedInformationText(GetInstallerExpandedInfoForVersion(version));

int nButton;
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
Expand All @@ -113,18 +149,19 @@ HRESULT CFxHelper::InstallDotNetFramework(bool isQuiet)
WCHAR szFinalTempFileName[_MAX_PATH] = L"";
CComPtr<IBindStatusCallback> bscb;
CComPtr<IProgressDialog> pd;
SHELLEXECUTEINFO execInfo = {sizeof(execInfo),};
SHELLEXECUTEINFO execInfo = { sizeof(execInfo), };

CString url;
url.LoadString(IDS_FXDOWNLOADURL);
url.LoadString(GetInstallerUrlForVersion(version));

WCHAR szTempPath[_MAX_PATH];
DWORD dwTempPathResult = GetTempPath(_MAX_PATH, szTempPath);

if (dwTempPathResult == 0) {
hr = AtlHresultFromLastError();
goto out;
} else if (dwTempPathResult > _MAX_PATH) {
}
else if (dwTempPathResult > _MAX_PATH) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this to the previous line

hr = DISP_E_BUFFERTOOSMALL;
goto out;
}
Expand All @@ -147,7 +184,8 @@ HRESULT CFxHelper::InstallDotNetFramework(bool isQuiet)
hr = E_FAIL;
goto out;
}
} else {
}
else {
if (wcscpy_s(pLastDot, _countof(szFinalTempFileName) - (pLastDot - szFinalTempFileName), L".exe") != 0) {
hr = E_FAIL;
goto out;
Expand Down Expand Up @@ -189,7 +227,8 @@ HRESULT CFxHelper::InstallDotNetFramework(bool isQuiet)

if (isQuiet) {
execInfo.lpParameters = L"/q /norestart";
} else {
}
else {
execInfo.lpParameters = L"/passive /norestart /showrmui";
}

Expand All @@ -212,7 +251,8 @@ HRESULT CFxHelper::InstallDotNetFramework(bool isQuiet)
// See https://msdn.microsoft.com/en-us/library/ee942965%28v=vs.110%29.aspx
hr = HandleRebootRequirement(isQuiet);
// Exit as a failure, so that setup doesn't carry on now
} else {
}
else {
hr = exitCode != 0 ? E_FAIL : S_OK;
}

Expand All @@ -229,15 +269,47 @@ HRESULT CFxHelper::InstallDotNetFramework(bool isQuiet)
return hr;
}

UINT CFxHelper::GetInstallerMainInstructionForVersion(NetVersion version)
{
if (version >= NetVersion::net46) {
return IDS_FXINSTRUCTION46;
}
return IDS_FXINSTRUCTION;
}

UINT CFxHelper::GetInstallerContentForVersion(NetVersion version)
{
if (version >= NetVersion::net46) {
return IDS_FXCONTENT46;
}
return IDS_FXCONTENT;
}

UINT CFxHelper::GetInstallerExpandedInfoForVersion(NetVersion version)
{
if (version >= NetVersion::net46) {
return IDS_FXEXPANDEDINFO46;
}
return IDS_FXEXPANDEDINFO;
}

UINT CFxHelper::GetInstallerUrlForVersion(NetVersion version)
{
if (version >= NetVersion::net46) {
return IDS_FXDOWNLOADURL46;
}
return IDS_FXDOWNLOADURL;
}

// Deal with the aftermath of the framework installer telling us that we need to reboot
HRESULT CFxHelper::HandleRebootRequirement(bool isQuiet)
{
if (isQuiet) {
// Don't silently reboot - just error-out
fprintf_s(stderr, "A reboot is required following .NET installation - reboot then run installer again.\n");
return E_FAIL;
}
}

CTaskDialog dlg;
TASKDIALOG_BUTTON buttons[] = {
{ 1, L"Restart Now", },
Expand All @@ -246,7 +318,7 @@ HRESULT CFxHelper::HandleRebootRequirement(bool isQuiet)

dlg.SetButtons(buttons, 2);
dlg.SetMainInstructionText(L"Restart System");
dlg.SetContentText(L"To finish installing the .NET Framework 4.5, the system now needs to restart. The installation will finish after you restart and log-in again.");
dlg.SetContentText(L"To finish installing the .NET Framework, the system now needs to restart. The installation will finish after you restart and log-in again.");
dlg.SetMainIcon(TD_INFORMATION_ICON);

dlg.SetExpandedInformationText(L"If you click 'Cancel', you'll need to re-run this setup program yourself, after restarting your system.");
Expand Down
12 changes: 10 additions & 2 deletions src/Setup/FxHelper.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
#pragma once

enum class NetVersion {net45=0, net451=1, net452=2, net46=3, net461=4, net462=5};

class CFxHelper
{
public:
static NetVersion GetRequiredDotNetVersion();
static bool CanInstallDotNet4_5();
static bool IsDotNet45OrHigherInstalled();
static HRESULT InstallDotNetFramework(bool isQuiet);
static bool IsDotNetInstalled(NetVersion requiredVersion);
static HRESULT InstallDotNetFramework(NetVersion version, bool isQuiet);
private:
static HRESULT HandleRebootRequirement(bool isQuiet);
static bool WriteRunOnceEntry();
static bool RebootSystem();
static int GetDotNetVersionReleaseNumber(NetVersion version);
static UINT GetInstallerUrlForVersion(NetVersion version);
static UINT GetInstallerMainInstructionForVersion(NetVersion version);
static UINT GetInstallerContentForVersion(NetVersion version);
static UINT GetInstallerExpandedInfoForVersion(NetVersion version);
};

Binary file modified src/Setup/Setup.rc
Binary file not shown.
3 changes: 2 additions & 1 deletion src/Setup/Setup.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,10 @@
<Image Include="small.ico" />
</ItemGroup>
<ItemGroup>
<None Include="flags1.bin" />
<None Include="update.zip" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
3 changes: 3 additions & 0 deletions src/Setup/Setup.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,8 @@
<None Include="update.zip">
<Filter>Resource Files</Filter>
</None>
<None Include="flags1.bin">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
</Project>
Binary file added src/Setup/flags1.bin
Binary file not shown.
Binary file modified src/Setup/resource.h
Binary file not shown.
8 changes: 5 additions & 3 deletions src/Setup/winmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
goto out;
}

if (!CFxHelper::IsDotNet45OrHigherInstalled()) {
hr = CFxHelper::InstallDotNetFramework(isQuiet);
NetVersion requiredVersion = CFxHelper::GetRequiredDotNetVersion();

if (!CFxHelper::IsDotNetInstalled(requiredVersion)) {
hr = CFxHelper::InstallDotNetFramework(requiredVersion, isQuiet);
if (FAILED(hr)) {
exitCode = hr; // #yolo
CUpdateRunner::DisplayErrorMessage(CString(L"Failed to install the .NET Framework, try installing .NET 4.5 or higher manually"), NULL);
CUpdateRunner::DisplayErrorMessage(CString(L"Failed to install the .NET Framework, try installing the latest version manually"), NULL);
goto out;
}

Expand Down
16 changes: 12 additions & 4 deletions src/Update/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ int executeCommandLine(string[] args)
string setupIcon = default(string);
string icon = default(string);
string shortcutArgs = default(string);
string frameworkVersion = default(string);
bool shouldWait = false;
bool noMsi = (Environment.OSVersion.Platform != PlatformID.Win32NT); // NB: WiX doesn't work under Mono / Wine

Expand Down Expand Up @@ -123,6 +124,7 @@ int executeCommandLine(string[] args)
{ "a=|process-start-args=", "Arguments that will be used when starting executable", v => processStartArgs = v, true},
{ "l=|shortcut-locations=", "Comma-separated string of shortcut locations, e.g. 'Desktop,StartMenu'", v => shortcutArgs = v},
{ "no-msi", "Don't generate an MSI package", v => noMsi = true},
{ "framework-version=", "Set the required .NET framework version, e.g. net461", v => frameworkVersion = v },
};

opts.Parse(args);
Expand Down Expand Up @@ -173,7 +175,7 @@ int executeCommandLine(string[] args)
break;
#endif
case UpdateAction.Releasify:
Releasify(target, releaseDir, packagesDir, bootstrapperExe, backgroundGif, signingParameters, baseUrl, setupIcon, !noMsi);
Releasify(target, releaseDir, packagesDir, bootstrapperExe, backgroundGif, signingParameters, baseUrl, setupIcon, !noMsi, frameworkVersion);
break;
}
}
Expand Down Expand Up @@ -332,7 +334,7 @@ public async Task Uninstall(string appName = null)
}
}

public void Releasify(string package, string targetDir = null, string packagesDir = null, string bootstrapperExe = null, string backgroundGif = null, string signingOpts = null, string baseUrl = null, string setupIcon = null, bool generateMsi = true)
public void Releasify(string package, string targetDir = null, string packagesDir = null, string bootstrapperExe = null, string backgroundGif = null, string signingOpts = null, string baseUrl = null, string setupIcon = null, bool generateMsi = true, string frameworkVersion = null)
{
if (baseUrl != null) {
if (!Utility.IsHttpUrl(baseUrl)) {
Expand Down Expand Up @@ -434,8 +436,14 @@ public void Releasify(string package, string targetDir = null, string packagesDi

var writeZipToSetup = findExecutable("WriteZipToSetup.exe");

try {
var result = Utility.InvokeProcessAsync(writeZipToSetup, String.Format("\"{0}\" \"{1}\"", targetSetupExe, zipPath), CancellationToken.None).Result;
try
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please match the existing code style with braces

{
var arguments = String.Format("\"{0}\" \"{1}\"", targetSetupExe, zipPath);
if (!String.IsNullOrEmpty(frameworkVersion))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just default this to .NET 4.5?

{
arguments += String.Format(" \"--set-required-framework\" \"{0}\"", frameworkVersion);
}
var result = Utility.InvokeProcessAsync(writeZipToSetup, arguments, CancellationToken.None).Result;
if (result.Item1 != 0) throw new Exception("Failed to write Zip to Setup.exe!\n\n" + result.Item2);
} catch (Exception ex) {
this.Log().ErrorException("Failed to update Setup.exe with new Zip file", ex);
Expand Down
14 changes: 12 additions & 2 deletions src/WriteZipToSetup/WriteZipToSetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ int wmain(int argc, wchar_t* argv[])
if (argc != 4) goto fail;
return CopyResourcesToStubExecutable(argv[2], argv[3]);
}

if (argc != 3) {
bool setFramework = false;
if (argc == 5 && wcscmp(argv[3], L"--set-required-framework") == 0) {
setFramework = true;
}
else if (argc != 3) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⬆️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused by what you'd like here. If --set-required-framework is unspecified, it will not overwrite the default value of net45, which is set in setup resources. Would you prefer WriteZipToSetup to always overwrite that value? Or are you suggesting making --set-required-framework a required flag for WriteZipToSetup?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, that's a style change...

goto fail;
}

Expand Down Expand Up @@ -107,6 +110,13 @@ int wmain(int argc, wchar_t* argv[])
goto fail;
}

if (setFramework) {
if (!UpdateResource(hRes, L"FLAGS", (LPCWSTR)132, 0x0409, argv[4], (wcslen(argv[4])+1) * sizeof(wchar_t))) {
printf("Failed to update resouce\n");
goto fail;
}
}

printf("Finished!\n");
if (!EndUpdateResource(hRes, false)) {
printf("Failed to update resource\n");
Expand Down