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
9using System;
10using System.Text;
11using System.Runtime.InteropServices;
12using System.Collections.Generic;
13
14namespace 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
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
37        void Skip([MarshalAs(UnmanagedType.U4), In] int celt);
38
39        void Reset();
40
41        [return: MarshalAs(UnmanagedType.Interface)]
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)]
59        IEnumSetupInstances EnumInstances();
60
61        [return: MarshalAs(UnmanagedType.Interface)]
62        ISetupInstance GetInstanceForCurrentProcess();
63
64        [return: MarshalAs(UnmanagedType.Interface)]
65        ISetupInstance GetInstanceForPath([MarshalAs(UnmanagedType.LPWStr), In] string path);
66
67        [return: MarshalAs(UnmanagedType.Interface)]
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)]
84        string GetInstanceId();
85
86        [return: MarshalAs(UnmanagedType.Struct)]
87        System.Runtime.InteropServices.ComTypes.FILETIME GetInstallDate();
88
89        [return: MarshalAs(UnmanagedType.BStr)]
90        string GetInstallationName();
91
92        [return: MarshalAs(UnmanagedType.BStr)]
93        string GetInstallationPath();
94
95        [return: MarshalAs(UnmanagedType.BStr)]
96        string GetInstallationVersion();
97
98        [return: MarshalAs(UnmanagedType.BStr)]
99        string GetDisplayName([MarshalAs(UnmanagedType.U4), In] int lcid);
100
101        [return: MarshalAs(UnmanagedType.BStr)]
102        string GetDescription([MarshalAs(UnmanagedType.U4), In] int lcid);
103
104        [return: MarshalAs(UnmanagedType.BStr)]
105        string ResolvePath([MarshalAs(UnmanagedType.LPWStr), In] string pwszRelativePath);
106
107        [return: MarshalAs(UnmanagedType.U4)]
108        InstanceState GetState();
109
110        [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)]
111        ISetupPackageReference[] GetPackages();
112
113        ISetupPackageReference GetProduct();
114
115        [return: MarshalAs(UnmanagedType.BStr)]
116        string GetProductPath();
117
118        [return: MarshalAs(UnmanagedType.VariantBool)]
119        bool IsLaunchable();
120
121        [return: MarshalAs(UnmanagedType.VariantBool)]
122        bool IsComplete();
123
124        [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)]
125        ISetupPropertyStore GetProperties();
126
127        [return: MarshalAs(UnmanagedType.BStr)]
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)]
138        string GetId();
139
140        [return: MarshalAs(UnmanagedType.BStr)]
141        string GetVersion();
142
143        [return: MarshalAs(UnmanagedType.BStr)]
144        string GetChip();
145
146        [return: MarshalAs(UnmanagedType.BStr)]
147        string GetLanguage();
148
149        [return: MarshalAs(UnmanagedType.BStr)]
150        string GetBranch();
151
152        [return: MarshalAs(UnmanagedType.BStr)]
153        string GetType();
154
155        [return: MarshalAs(UnmanagedType.BStr)]
156        string GetUniqueId();
157
158        [return: MarshalAs(UnmanagedType.VariantBool)]
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)]
169        string[] GetNames();
170
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    {
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
219        private static string JsonString(string s)
220        {
221            return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
222        }
223
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