1 // Copyright 2017 - Refael Ackermann 2 // Distributed under MIT style license 3 // See accompanying file LICENSE at https://github.com/node4good/windows-autoconf 4 5 // Usage: 6 // powershell -ExecutionPolicy Unrestricted -Command "Add-Type -Path Find-VisualStudio.cs; [VisualStudioConfiguration.Main]::PrintJson()" 7 // This script needs to be compatible with PowerShell v2 to run on Windows 2008R2 and Windows 7. 8 9 using System; 10 using System.Text; 11 using System.Runtime.InteropServices; 12 using System.Collections.Generic; 13 14 namespace VisualStudioConfiguration 15 { 16 [Flags] 17 public enum InstanceState : uint 18 { 19 None = 0, 20 Local = 1, 21 Registered = 2, 22 NoRebootRequired = 4, 23 NoErrors = 8, 24 Complete = 4294967295, 25 } 26 27 [Guid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")] 28 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 29 [ComImport] 30 public interface IEnumSetupInstances 31 { 32 Next([MarshalAs(UnmanagedType.U4), In] int celt, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface), Out] ISetupInstance[] rgelt, [MarshalAs(UnmanagedType.U4)] out int pceltFetched)33 void Next([MarshalAs(UnmanagedType.U4), In] int celt, 34 [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface), Out] ISetupInstance[] rgelt, 35 [MarshalAs(UnmanagedType.U4)] out int pceltFetched); 36 Skip([MarshalAs(UnmanagedType.U4), In] int celt)37 void Skip([MarshalAs(UnmanagedType.U4), In] int celt); 38 Reset()39 void Reset(); 40 41 [return: MarshalAs(UnmanagedType.Interface)] Clone()42 IEnumSetupInstances Clone(); 43 } 44 45 [Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")] 46 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 47 [ComImport] 48 public interface ISetupConfiguration 49 { 50 } 51 52 [Guid("26AAB78C-4A60-49D6-AF3B-3C35BC93365D")] 53 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 54 [ComImport] 55 public interface ISetupConfiguration2 : ISetupConfiguration 56 { 57 58 [return: MarshalAs(UnmanagedType.Interface)] EnumInstances()59 IEnumSetupInstances EnumInstances(); 60 61 [return: MarshalAs(UnmanagedType.Interface)] GetInstanceForCurrentProcess()62 ISetupInstance GetInstanceForCurrentProcess(); 63 64 [return: MarshalAs(UnmanagedType.Interface)] GetInstanceForPath([MarshalAs(UnmanagedType.LPWStr), In] string path)65 ISetupInstance GetInstanceForPath([MarshalAs(UnmanagedType.LPWStr), In] string path); 66 67 [return: MarshalAs(UnmanagedType.Interface)] EnumAllInstances()68 IEnumSetupInstances EnumAllInstances(); 69 } 70 71 [Guid("B41463C3-8866-43B5-BC33-2B0676F7F42E")] 72 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 73 [ComImport] 74 public interface ISetupInstance 75 { 76 } 77 78 [Guid("89143C9A-05AF-49B0-B717-72E218A2185C")] 79 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 80 [ComImport] 81 public interface ISetupInstance2 : ISetupInstance 82 { 83 [return: MarshalAs(UnmanagedType.BStr)] GetInstanceId()84 string GetInstanceId(); 85 86 [return: MarshalAs(UnmanagedType.Struct)] GetInstallDate()87 System.Runtime.InteropServices.ComTypes.FILETIME GetInstallDate(); 88 89 [return: MarshalAs(UnmanagedType.BStr)] GetInstallationName()90 string GetInstallationName(); 91 92 [return: MarshalAs(UnmanagedType.BStr)] GetInstallationPath()93 string GetInstallationPath(); 94 95 [return: MarshalAs(UnmanagedType.BStr)] GetInstallationVersion()96 string GetInstallationVersion(); 97 98 [return: MarshalAs(UnmanagedType.BStr)] GetDisplayName([MarshalAs(UnmanagedType.U4), In] int lcid)99 string GetDisplayName([MarshalAs(UnmanagedType.U4), In] int lcid); 100 101 [return: MarshalAs(UnmanagedType.BStr)] GetDescription([MarshalAs(UnmanagedType.U4), In] int lcid)102 string GetDescription([MarshalAs(UnmanagedType.U4), In] int lcid); 103 104 [return: MarshalAs(UnmanagedType.BStr)] ResolvePath([MarshalAs(UnmanagedType.LPWStr), In] string pwszRelativePath)105 string ResolvePath([MarshalAs(UnmanagedType.LPWStr), In] string pwszRelativePath); 106 107 [return: MarshalAs(UnmanagedType.U4)] GetState()108 InstanceState GetState(); 109 110 [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] GetPackages()111 ISetupPackageReference[] GetPackages(); 112 GetProduct()113 ISetupPackageReference GetProduct(); 114 115 [return: MarshalAs(UnmanagedType.BStr)] GetProductPath()116 string GetProductPath(); 117 118 [return: MarshalAs(UnmanagedType.VariantBool)] IsLaunchable()119 bool IsLaunchable(); 120 121 [return: MarshalAs(UnmanagedType.VariantBool)] IsComplete()122 bool IsComplete(); 123 124 [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] GetProperties()125 ISetupPropertyStore GetProperties(); 126 127 [return: MarshalAs(UnmanagedType.BStr)] GetEnginePath()128 string GetEnginePath(); 129 } 130 131 [Guid("DA8D8A16-B2B6-4487-A2F1-594CCCCD6BF5")] 132 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 133 [ComImport] 134 public interface ISetupPackageReference 135 { 136 137 [return: MarshalAs(UnmanagedType.BStr)] GetId()138 string GetId(); 139 140 [return: MarshalAs(UnmanagedType.BStr)] GetVersion()141 string GetVersion(); 142 143 [return: MarshalAs(UnmanagedType.BStr)] GetChip()144 string GetChip(); 145 146 [return: MarshalAs(UnmanagedType.BStr)] GetLanguage()147 string GetLanguage(); 148 149 [return: MarshalAs(UnmanagedType.BStr)] GetBranch()150 string GetBranch(); 151 152 [return: MarshalAs(UnmanagedType.BStr)] GetType()153 string GetType(); 154 155 [return: MarshalAs(UnmanagedType.BStr)] GetUniqueId()156 string GetUniqueId(); 157 158 [return: MarshalAs(UnmanagedType.VariantBool)] GetIsExtension()159 bool GetIsExtension(); 160 } 161 162 [Guid("c601c175-a3be-44bc-91f6-4568d230fc83")] 163 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 164 [ComImport] 165 public interface ISetupPropertyStore 166 { 167 168 [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] GetNames()169 string[] GetNames(); 170 GetValue([MarshalAs(UnmanagedType.LPWStr), In] string pwszName)171 object GetValue([MarshalAs(UnmanagedType.LPWStr), In] string pwszName); 172 } 173 174 [Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")] 175 [CoClass(typeof(SetupConfigurationClass))] 176 [ComImport] 177 public interface SetupConfiguration : ISetupConfiguration2, ISetupConfiguration 178 { 179 } 180 181 [Guid("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")] 182 [ClassInterface(ClassInterfaceType.None)] 183 [ComImport] 184 public class SetupConfigurationClass 185 { 186 } 187 188 public static class Main 189 { PrintJson()190 public static void PrintJson() 191 { 192 ISetupConfiguration query = new SetupConfiguration(); 193 ISetupConfiguration2 query2 = (ISetupConfiguration2)query; 194 IEnumSetupInstances e = query2.EnumAllInstances(); 195 196 int pceltFetched; 197 ISetupInstance2[] rgelt = new ISetupInstance2[1]; 198 List<string> instances = new List<string>(); 199 while (true) 200 { 201 e.Next(1, rgelt, out pceltFetched); 202 if (pceltFetched <= 0) 203 { 204 Console.WriteLine(String.Format("[{0}]", string.Join(",", instances.ToArray()))); 205 return; 206 } 207 208 try 209 { 210 instances.Add(InstanceJson(rgelt[0])); 211 } 212 catch (COMException) 213 { 214 // Ignore instances that can't be queried. 215 } 216 } 217 } 218 JsonString(string s)219 private static string JsonString(string s) 220 { 221 return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; 222 } 223 InstanceJson(ISetupInstance2 setupInstance2)224 private static string InstanceJson(ISetupInstance2 setupInstance2) 225 { 226 // Visual Studio component directory: 227 // https://docs.microsoft.com/en-us/visualstudio/install/workload-and-component-ids 228 229 StringBuilder json = new StringBuilder(); 230 json.Append("{"); 231 232 string path = JsonString(setupInstance2.GetInstallationPath()); 233 json.Append(String.Format("\"path\":{0},", path)); 234 235 string version = JsonString(setupInstance2.GetInstallationVersion()); 236 json.Append(String.Format("\"version\":{0},", version)); 237 238 List<string> packages = new List<string>(); 239 foreach (ISetupPackageReference package in setupInstance2.GetPackages()) 240 { 241 string id = JsonString(package.GetId()); 242 packages.Add(id); 243 } 244 json.Append(String.Format("\"packages\":[{0}]", string.Join(",", packages.ToArray()))); 245 246 json.Append("}"); 247 return json.ToString(); 248 } 249 } 250 } 251