55using System . Collections . Generic ;
66using System . Linq ;
77using System . Runtime . InteropServices ;
8+ using System . Text . RegularExpressions ;
89using Microsoft . DotNet . Tools . Uninstall . MacOs ;
910using Microsoft . DotNet . Tools . Uninstall . Shared . BundleInfo ;
1011using Microsoft . DotNet . Tools . Uninstall . Shared . BundleInfo . Versioning ;
@@ -42,7 +43,7 @@ public virtual IEnumerable<Bundle> GetAllInstalledBundles()
4243
4344 var bundles = names
4445 . Select ( name => uninstalls . OpenSubKey ( name ) )
45- . Where ( bundle => IsDotNetCoreBundle ( bundle ) ) ;
46+ . Where ( bundle => IsNetCoreBundle ( bundle ) ) ;
4647
4748 var wrappedBundles = bundles
4849 . Select ( bundle => WrapRegistryKey ( bundle ) )
@@ -52,36 +53,34 @@ public virtual IEnumerable<Bundle> GetAllInstalledBundles()
5253 return wrappedBundles ;
5354 }
5455
55- private static bool IsDotNetCoreBundle ( RegistryKey registryKey )
56+ private static bool IsNetCoreBundle ( RegistryKey uninstallKey )
5657 {
57- return IsDotNetCoreBundleDisplayName ( registryKey . GetValue ( "DisplayName" ) as string )
58- && IsDotNetCoreBundlePublisher ( registryKey . GetValue ( "Publisher" ) as string )
59- && IsDotNetCoreBundleUninstaller ( registryKey . GetValue ( "WindowsInstaller" ) as int ? )
60- && IsNotVisualStudioDummyVersion ( registryKey . GetValue ( "DisplayName" ) as string ) ;
61- }
62-
63- private static bool IsNotVisualStudioDummyVersion ( string displayName )
64- {
65- return ! displayName . Contains ( " from Visual Studio" ) ;
66- }
67-
68- private static bool IsDotNetCoreBundleDisplayName ( string displayName )
69- {
70- return displayName == null ?
71- false :
72- Regexes . BundleDisplayNameRegex . IsMatch ( displayName ) ;
73- }
58+ if ( uninstallKey == null )
59+ {
60+ return false ;
61+ }
7462
75- private static bool IsDotNetCoreBundlePublisher ( string publisher )
76- {
77- return publisher == null ?
78- false :
79- Regexes . BundlePublisherRegex . IsMatch ( publisher ) ;
63+ return IsNetCoreBundle ( uninstallKey . GetValue ( "DisplayName" ) as string ,
64+ uninstallKey . GetValue ( "DisplayVersion" ) as string ,
65+ uninstallKey . GetValue ( "UninstallString" ) as string ,
66+ uninstallKey . GetValue ( "BundleVersion" ) as string ) ;
8067 }
8168
82- private static bool IsDotNetCoreBundleUninstaller ( int ? windowsInstaller )
69+ internal static bool IsNetCoreBundle ( string displayName , string displayVersion , string uninstallString , string bundleVersion )
8370 {
84- return windowsInstaller == null ;
71+ return ( ! String . IsNullOrEmpty ( displayName ) ) &&
72+ ( displayName . IndexOf ( "Visual Studio" , StringComparison . OrdinalIgnoreCase ) < 0 ) &&
73+ ( displayName . IndexOf ( "VS 2015" , StringComparison . OrdinalIgnoreCase ) < 0 ) &&
74+ ( displayName . IndexOf ( "Local Feed" , StringComparison . OrdinalIgnoreCase ) < 0 ) &&
75+ ( ( displayName . IndexOf ( ".NET Core" , StringComparison . OrdinalIgnoreCase ) >= 0 ) ||
76+ ( displayName . IndexOf ( ".NET Runtime" , StringComparison . OrdinalIgnoreCase ) >= 0 ) ||
77+ ( displayName . IndexOf ( ".NET SDK" , StringComparison . OrdinalIgnoreCase ) >= 0 ) ||
78+ ( displayName . IndexOf ( "Dotnet Shared Framework for Windows Desktop" , StringComparison . OrdinalIgnoreCase ) >= 0 ) ) &&
79+ ( ! String . IsNullOrEmpty ( uninstallString ) ) &&
80+ ( uninstallString . IndexOf ( ".exe" , StringComparison . OrdinalIgnoreCase ) >= 0 ) &&
81+ ( uninstallString . IndexOf ( "msiexec" , StringComparison . OrdinalIgnoreCase ) < 0 ) &&
82+ ( ! String . IsNullOrEmpty ( displayVersion ) ) &&
83+ ( ! String . IsNullOrEmpty ( bundleVersion ) ) ;
8584 }
8685
8786 private static Bundle WrapRegistryKey ( RegistryKey registryKey )
@@ -90,7 +89,8 @@ private static Bundle WrapRegistryKey(RegistryKey registryKey)
9089 var uninstallCommand = registryKey . GetValue ( "QuietUninstallString" ) as string ;
9190 var bundleCachePath = registryKey . GetValue ( "BundleCachePath" ) as string ;
9291
93- ParseVersionAndArch ( registryKey , displayName , bundleCachePath , out var version , out var arch ) ;
92+ var version = GetBundleVersion ( displayName , uninstallCommand , bundleCachePath ) ;
93+ var arch = GetBundleArch ( displayName , bundleCachePath ) ;
9494
9595 if ( version == null )
9696 {
@@ -100,41 +100,59 @@ private static Bundle WrapRegistryKey(RegistryKey registryKey)
100100 return Bundle . From ( version , arch , uninstallCommand , displayName ) ;
101101 }
102102
103- private static void ParseVersionAndArch ( RegistryKey registryKey , string displayName , string bundleCachePath , out BundleVersion version , out BundleArch arch )
103+ private static BundleVersion GetBundleVersion ( string displayName , string uninstallString , string bundleCachePath )
104104 {
105- var match = Regexes . BundleDisplayNameRegex . Match ( displayName ) ;
105+ var versionString = Regexes . VersionDisplayNameRegex . Match ( displayName ) ? . Value ?? string . Empty ;
106106 var cachePathMatch = Regexes . BundleCachePathRegex . Match ( bundleCachePath ) ;
107- var archString = cachePathMatch . Groups [ Regexes . ArchGroupName ] . Value ?? string . Empty ;
108- var versionFromCachePath = cachePathMatch . Groups [ Regexes . VersionGroupName ] . Value ;
109- // Note: ASP.NET Core runtimes do not include version in the cache path, need to get version from registry:
110- var versionFromRegistry = string . Join ( '.' , ( registryKey . GetValue ( "DisplayVersion" ) as string ) . Split ( '.' ) . Take ( 3 ) ) ;
111- var versionString = string . IsNullOrEmpty ( versionFromCachePath ) ? versionFromRegistry : versionFromCachePath ;
112107 var hasAuxVersion = cachePathMatch . Groups [ Regexes . AuxVersionGroupName ] . Success ;
113108 var footnote = hasAuxVersion ?
114109 string . Format ( LocalizableStrings . HostingBundleFootnoteFormat , displayName , versionString ) :
115110 null ;
116111
117- if ( string . IsNullOrEmpty ( displayName ) || string . IsNullOrEmpty ( versionString ) )
112+ // Classify the bundle type
113+ if ( displayName . IndexOf ( "Windows Server" , StringComparison . OrdinalIgnoreCase ) >= 0 )
118114 {
119- version = null ;
120- arch = BundleArch . X64 | BundleArch . X86 ;
121- return ;
115+ return new HostingBundleVersion ( versionString , footnote ) ;
122116 }
117+ else if ( displayName . IndexOf ( "ASP.NET" , StringComparison . OrdinalIgnoreCase ) >= 0 )
118+ {
119+ return new AspNetRuntimeVersion ( versionString ) ;
120+ }
121+ else if ( ( displayName . IndexOf ( ".NET Core SDK" , StringComparison . OrdinalIgnoreCase ) >= 0 ) ||
122+ ( displayName . IndexOf ( "Microsoft .NET SDK" , StringComparison . OrdinalIgnoreCase ) >= 0 ) ||
123+ uninstallString . IndexOf ( "dotnet-dev-win" ) >= 0 )
124+ {
125+ return new SdkVersion ( versionString ) ;
126+ }
127+ else if ( displayName . IndexOf ( ".NET Core Runtime" , StringComparison . OrdinalIgnoreCase ) >= 0 || Regex . IsMatch ( displayName , @".*\.NET Core.*Runtime" ) ||
128+ displayName . IndexOf ( ".NET Runtime" , StringComparison . OrdinalIgnoreCase ) >= 0 )
129+ {
130+ return new RuntimeVersion ( versionString ) ;
131+ }
132+ else {
133+ return null ;
134+ }
135+ }
123136
124- switch ( match . Groups [ Regexes . TypeGroupName ] . Value )
137+ private static BundleArch GetBundleArch ( string displayName , string bundleCachePath )
138+ {
139+ const string x64String = "x64" ;
140+ const string x86String = "x86" ;
141+
142+ var cachePathMatch = Regexes . BundleCachePathRegex . Match ( bundleCachePath ) ;
143+
144+ var archString = cachePathMatch . Groups [ Regexes . ArchGroupName ] . Value ;
145+
146+ if ( string . IsNullOrEmpty ( archString ) )
125147 {
126- case "SDK" : version = new SdkVersion ( versionString ) ; break ;
127- case "Runtime" : version = new RuntimeVersion ( versionString ) ; break ;
128- case "ASP.NET" : version = new AspNetRuntimeVersion ( versionString ) ; break ;
129- case "Windows Server Hosting" : version = new HostingBundleVersion ( versionString , footnote ) ; break ;
130- default : throw new ArgumentException ( ) ;
148+ archString = displayName . Contains ( x64String ) ? x64String : displayName . Contains ( x86String ) ? x86String : string . Empty ;
131149 }
132150
133151 switch ( archString )
134152 {
135- case "x64" : arch = BundleArch . X64 ; break ;
136- case "x86" : arch = BundleArch . X86 ; break ;
137- case "" : arch = BundleArch . X64 | BundleArch . X86 ; break ;
153+ case x64String : return BundleArch . X64 ;
154+ case x86String : return BundleArch . X86 ;
155+ case "" : return BundleArch . X64 | BundleArch . X86 ;
138156 default : throw new ArgumentException ( ) ;
139157 }
140158 }
0 commit comments