@@ -8,13 +8,13 @@ public static class DependencyInstaller
88 /// Gets all URLs to missing runtime files.
99 /// </summary>
1010 /// <param name="reloadedFolderPath">Folder path for an application, library will check every runtimeconfig.json inside this folder.</param>
11- public static async Task < HashSet < UrlCommandlinePair > > GetMissingRuntimeUrls ( string reloadedFolderPath )
11+ public static async Task < HashSet < MissingDependency > > GetMissingDependencies ( string reloadedFolderPath )
1212 {
1313 var allFiles = Directory . GetFiles ( reloadedFolderPath , "*.*" , SearchOption . AllDirectories ) ;
1414 var filesWithRuntimeConfigs = allFiles . Where ( x => x . EndsWith ( ".runtimeconfig.json" ) ) . ToArray ( ) ;
1515
1616 // Now that we have the runtime config files, get all missing deps from them.
17- var urlSet = new HashSet < UrlCommandlinePair > ( ) ;
17+ var urlSet = new HashSet < MissingDependency > ( ) ;
1818 var dotNetCommandLineParams = new string [ ]
1919 {
2020 "/install" ,
@@ -30,38 +30,61 @@ public static async Task<HashSet<UrlCommandlinePair>> GetMissingRuntimeUrls(stri
3030 foreach ( var runtimeConfig in filesWithRuntimeConfigs )
3131 {
3232 var runtimeOptions = RuntimeOptions . FromFile ( runtimeConfig ) ;
33- var result = resolver . Resolve ( runtimeOptions ) ; ;
33+ var result = resolver . Resolve ( runtimeOptions ) ;
3434 foreach ( var missingDep in result . MissingDependencies )
3535 {
3636 var frameworkDownloader = new FrameworkDownloader ( runtimeOptions . GetAllFrameworks ( ) [ 0 ] . NuGetVersion , missingDep . FrameworkName ) ;
37- urlSet . Add ( new UrlCommandlinePair ( )
37+ urlSet . Add ( new MissingDependency ( )
3838 {
39- FriendlyName = $ ".NET Core { runtimeOptions . GetAllFrameworks ( ) [ 0 ] . Version } { architecture } ",
40- Url = await frameworkDownloader . GetDownloadUrlAsync ( architecture ) ,
41- Parameters = dotNetCommandLineParams
39+ FriendlyName = $ ".NET Core { missingDep . Version } { missingDep . FrameworkName } ({ architecture } )",
40+ Url = await frameworkDownloader . GetDownloadUrlAsync ( architecture , Platform . Windows , Format . Executable , false ) ,
41+ Parameters = dotNetCommandLineParams ,
42+ NetFrameworkName = missingDep . FrameworkName ,
43+ Version = missingDep . Version ,
44+ Architecture = architecture
4245 } ) ;
4346 }
4447 }
4548 }
4649
50+ // Filter out redundant .NET Runtime when .NET Desktop Runtime exists
51+ var groupedDeps = urlSet
52+ . Where ( dep => dep . NetFrameworkName . HasValue )
53+ . GroupBy ( dep => new { dep . Version , dep . Architecture } ) ;
54+
55+ foreach ( var group in groupedDeps )
56+ {
57+ var depsInGroup = group . ToList ( ) ;
58+ var hasDesktopRuntime = depsInGroup . Any ( dep => dep . NetFrameworkName == FrameworkName . WindowsDesktop ) ;
59+ var appRuntimeDeps = depsInGroup . Where ( dep => dep . NetFrameworkName == FrameworkName . App ) . ToList ( ) ;
60+
61+ if ( hasDesktopRuntime && appRuntimeDeps . Any ( ) )
62+ {
63+ foreach ( var redundantDep in appRuntimeDeps )
64+ urlSet . Remove ( redundantDep ) ;
65+ }
66+ }
67+
4768 // Also check for C++ Runtime.
4869 if ( ! RedistributablePackage . IsInstalled ( RedistributablePackageVersion . VC2015to2019x64 ) )
4970 {
50- urlSet . Add ( new UrlCommandlinePair ( )
71+ urlSet . Add ( new MissingDependency ( )
5172 {
5273 FriendlyName = "Visual C++ Redist x64" ,
5374 Url = "https://aka.ms/vs/17/release/vc_redist.x64.exe" ,
54- Parameters = dotNetCommandLineParams
75+ Parameters = dotNetCommandLineParams ,
76+ Architecture = Architecture . Amd64
5577 } ) ;
5678 }
5779
5880 if ( ! RedistributablePackage . IsInstalled ( RedistributablePackageVersion . VC2015to2019x86 ) )
5981 {
60- urlSet . Add ( new UrlCommandlinePair ( )
82+ urlSet . Add ( new MissingDependency ( )
6183 {
6284 FriendlyName = "Visual C++ Redist x86" ,
6385 Url = "https://aka.ms/vs/17/release/vc_redist.x86.exe" ,
64- Parameters = dotNetCommandLineParams
86+ Parameters = dotNetCommandLineParams ,
87+ Architecture = Architecture . x86
6588 } ) ;
6689 }
6790
@@ -81,7 +104,7 @@ public static async Task CheckAndInstallMissingRuntimesAsync(string folderPath,
81104 {
82105 // Get files with runtime configurations.
83106 setCurrentStepDescription ? . Invoke ( "Searching for Missing Runtimes" ) ;
84- var urlSet = await GetMissingRuntimeUrls ( folderPath ) ;
107+ var urlSet = await GetMissingDependencies ( folderPath ) ;
85108
86109 await DownloadAndInstallRuntimesAsync ( urlSet , downloadDirPath , progress , setCurrentStepDescription , token ) ;
87110 }
@@ -91,21 +114,22 @@ public static async Task CheckAndInstallMissingRuntimesAsync(string folderPath,
91114 /// [Requires Admin Permission]
92115 /// Finds all missing runtimes and silently installs them.
93116 /// </summary>
94- /// <param name="urlSet">Set of all URLs and commandline arguments for the downloaded files. Obtained form running <see cref="GetMissingRuntimeUrls "/>.</param>
117+ /// <param name="urlSet">Set of all URLs and commandline arguments for the downloaded files. Obtained form running <see cref="GetMissingDependencies "/>.</param>
95118 /// <param name="downloadDirPath">Path to where the temporary runtimes should be downloaded to.</param>
96119 /// <param name="progress">Progress for the installation operation.</param>
97120 /// <param name="setCurrentStepDescription">Shows a description of the current runtime installation step.</param>
98121 /// <param name="token">Token allowing you to cancel the install task.</param>
99- public static async Task DownloadAndInstallRuntimesAsync ( HashSet < UrlCommandlinePair > urlSet , string downloadDirPath , IProgress < double > progress , Action < string > setCurrentStepDescription , CancellationToken token = default )
122+ public static async Task DownloadAndInstallRuntimesAsync ( IEnumerable < MissingDependency > urlSet , string downloadDirPath , IProgress < double > progress , Action < string > setCurrentStepDescription , CancellationToken token = default )
100123 {
101124 // Download Runtimes
102- setCurrentStepDescription ? . Invoke ( "Downloading & Installing Runtimes" ) ;
125+ setCurrentStepDescription ? . Invoke ( "Downloading Runtimes... " ) ;
103126 var progressSlicer = new ProgressSlicer ( progress ) ;
104127 var downloadSlice = progressSlicer . Slice ( 0.8f ) ;
105128 var installSlice = progressSlicer . Slice ( 0.2f ) ;
106129
107- var downloadQueue = new ConcurrentQueue < UrlCommandlinePair > ( urlSet ) ;
108- var installQueue = new ConcurrentQueue < UrlCommandlinePair > ( ) ;
130+ var downloadQueue = new ConcurrentQueue < MissingDependency > ( urlSet ) ;
131+ var numItems = downloadQueue . Count ;
132+ var installQueue = new ConcurrentQueue < MissingDependency > ( ) ;
109133
110134 // Start background download and install.
111135 var downloadTask = DownloadAllRuntimesAsync ( ) ;
@@ -116,7 +140,7 @@ async Task DownloadAllRuntimesAsync()
116140 var downloadSlicer = new ProgressSlicer ( downloadSlice ) ;
117141 int x = 0 ;
118142
119- while ( downloadQueue . TryDequeue ( out UrlCommandlinePair downloadLink ) )
143+ while ( downloadQueue . TryDequeue ( out MissingDependency downloadLink ) )
120144 {
121145 var client = new WebClient ( ) ;
122146 var downloadProgress = downloadSlicer . Slice ( 1.0 / queueElements ) ;
@@ -130,8 +154,9 @@ async Task DownloadAllRuntimesAsync()
130154 if ( token . IsCancellationRequested )
131155 throw new TaskCanceledException ( ) ;
132156
133- installQueue . Enqueue ( new UrlCommandlinePair ( )
157+ installQueue . Enqueue ( new MissingDependency ( )
134158 {
159+ FriendlyName = downloadLink . FriendlyName ,
135160 Parameters = downloadLink . Parameters ,
136161 Url = filePath
137162 } ) ;
@@ -142,12 +167,13 @@ async Task DownloadAllRuntimesAsync()
142167 async Task InstallAllRuntimesAsync ( )
143168 {
144169 var installSlicer = new ProgressSlicer ( installSlice ) ;
145- var queueElements = urlSet . Count ;
170+ var queueElements = numItems ;
146171
147172 loopstart :
148- while ( installQueue . TryDequeue ( out UrlCommandlinePair runtimeInstallPath ) )
173+ while ( installQueue . TryDequeue ( out MissingDependency runtimeInstallPath ) )
149174 {
150175 var installProgress = installSlicer . Slice ( 1.0 / queueElements ) ;
176+ setCurrentStepDescription ? . Invoke ( "Installing Runtime: " + runtimeInstallPath . FriendlyName ) ;
151177 ExecuteAsAdmin ( runtimeInstallPath . Url , runtimeInstallPath . Parameters ) ;
152178 installProgress . Report ( 1 ) ;
153179 }
@@ -190,21 +216,66 @@ private static void SetArguments(this ProcessStartInfo processStartInfo, IEnumer
190216 }
191217}
192218
193- public class UrlCommandlinePair
219+ public class MissingDependency : IEquatable < MissingDependency >
194220{
195221 /// <summary>
196222 /// [Optional]
197223 /// Friendly name for the installation.
198224 /// </summary>
199- public string FriendlyName ;
225+ public string FriendlyName { get ; set ; }
200226
201227 /// <summary>
202228 /// URL for the runtime to be downloaded, or for the extracted runtime.
203229 /// </summary>
204- public string Url ;
230+ public string Url { get ; set ; }
231+
232+ /// <summary>
233+ /// Architecture of the runtime to be downloaded.
234+ /// </summary>
235+ public Architecture Architecture { get ; set ; }
205236
206237 /// <summary>
207238 /// Commandline parameters to install the runtime components.
208239 /// </summary>
209- public IEnumerable < string > Parameters ;
240+ public IEnumerable < string > Parameters { get ; set ; }
241+
242+ /// <summary>
243+ /// Name of the .NET Framework, e.g. "Microsoft.WindowsDesktop.App".
244+ /// </summary>
245+ public FrameworkName ? NetFrameworkName { get ; set ; }
246+
247+ /// <summary>
248+ /// Optional version (if known).
249+ /// </summary>
250+ public string Version { get ; set ; }
251+
252+ // Autogenerated by R#
253+ public bool Equals ( MissingDependency other )
254+ {
255+ if ( other is null ) return false ;
256+ if ( ReferenceEquals ( this , other ) ) return true ;
257+ return FriendlyName == other . FriendlyName && Url == other . Url && Architecture == other . Architecture && Equals ( Parameters , other . Parameters ) && NetFrameworkName == other . NetFrameworkName && Version == other . Version ;
258+ }
259+
260+ public override bool Equals ( object obj )
261+ {
262+ if ( obj is null ) return false ;
263+ if ( ReferenceEquals ( this , obj ) ) return true ;
264+ if ( obj . GetType ( ) != GetType ( ) ) return false ;
265+ return Equals ( ( MissingDependency ) obj ) ;
266+ }
267+
268+ public override int GetHashCode ( )
269+ {
270+ unchecked
271+ {
272+ var hashCode = ( FriendlyName != null ? FriendlyName . GetHashCode ( ) : 0 ) ;
273+ hashCode = ( hashCode * 397 ) ^ ( Url != null ? Url . GetHashCode ( ) : 0 ) ;
274+ hashCode = ( hashCode * 397 ) ^ ( int ) Architecture ;
275+ hashCode = ( hashCode * 397 ) ^ ( Parameters != null ? Parameters . GetHashCode ( ) : 0 ) ;
276+ hashCode = ( hashCode * 397 ) ^ NetFrameworkName . GetHashCode ( ) ;
277+ hashCode = ( hashCode * 397 ) ^ ( Version != null ? Version . GetHashCode ( ) : 0 ) ;
278+ return hashCode ;
279+ }
280+ }
210281}
0 commit comments