blob: ecc68e82e49f603eaf51860827b2928b2c8f80e0 [file] [log] [blame]
Steve Dower05f01d82017-09-07 11:49:23 -07001//
2// Helper library for location Visual Studio installations
3// using the COM-based query API.
4//
5// Copyright (c) Microsoft Corporation
6// Licensed to PSF under a contributor agreement
7//
8
9// Version history
10// 2017-05: Initial contribution (Steve Dower)
11
12#include <Windows.h>
13#include <Strsafe.h>
14#include "external\include\Setup.Configuration.h"
Steve Dower05f01d82017-09-07 11:49:23 -070015
16#include <Python.h>
17
18static PyObject *error_from_hr(HRESULT hr)
19{
20 if (FAILED(hr))
21 PyErr_Format(PyExc_OSError, "Error %08x", hr);
22 assert(PyErr_Occurred());
23 return nullptr;
24}
25
26static PyObject *get_install_name(ISetupInstance2 *inst)
27{
28 HRESULT hr;
29 BSTR name;
30 PyObject *str = nullptr;
31 if (FAILED(hr = inst->GetDisplayName(LOCALE_USER_DEFAULT, &name)))
32 goto error;
33 str = PyUnicode_FromWideChar(name, SysStringLen(name));
34 SysFreeString(name);
35 return str;
36error:
37
38 return error_from_hr(hr);
39}
40
41static PyObject *get_install_version(ISetupInstance *inst)
42{
43 HRESULT hr;
44 BSTR ver;
45 PyObject *str = nullptr;
46 if (FAILED(hr = inst->GetInstallationVersion(&ver)))
47 goto error;
48 str = PyUnicode_FromWideChar(ver, SysStringLen(ver));
49 SysFreeString(ver);
50 return str;
51error:
52
53 return error_from_hr(hr);
54}
55
56static PyObject *get_install_path(ISetupInstance *inst)
57{
58 HRESULT hr;
59 BSTR path;
60 PyObject *str = nullptr;
61 if (FAILED(hr = inst->GetInstallationPath(&path)))
62 goto error;
63 str = PyUnicode_FromWideChar(path, SysStringLen(path));
64 SysFreeString(path);
65 return str;
66error:
67
68 return error_from_hr(hr);
69}
70
71static PyObject *get_installed_packages(ISetupInstance2 *inst)
72{
73 HRESULT hr;
74 PyObject *res = nullptr;
75 LPSAFEARRAY sa_packages = nullptr;
76 LONG ub = 0;
77 IUnknown **packages = nullptr;
78 PyObject *str = nullptr;
79
80 if (FAILED(hr = inst->GetPackages(&sa_packages)) ||
81 FAILED(hr = SafeArrayAccessData(sa_packages, (void**)&packages)) ||
82 FAILED(SafeArrayGetUBound(sa_packages, 1, &ub)) ||
83 !(res = PyList_New(0)))
84 goto error;
85
86 for (LONG i = 0; i < ub; ++i) {
87 ISetupPackageReference *package = nullptr;
88 BSTR id = nullptr;
89 PyObject *str = nullptr;
90
91 if (FAILED(hr = packages[i]->QueryInterface(&package)) ||
92 FAILED(hr = package->GetId(&id)))
93 goto iter_error;
94
95 str = PyUnicode_FromWideChar(id, SysStringLen(id));
96 SysFreeString(id);
97
98 if (!str || PyList_Append(res, str) < 0)
99 goto iter_error;
100
101 Py_CLEAR(str);
102 package->Release();
103 continue;
104
105 iter_error:
106 if (package) package->Release();
107 Py_XDECREF(str);
108
109 goto error;
110 }
111
112 SafeArrayUnaccessData(sa_packages);
113 SafeArrayDestroy(sa_packages);
114
115 return res;
116error:
117 if (sa_packages && packages) SafeArrayUnaccessData(sa_packages);
118 if (sa_packages) SafeArrayDestroy(sa_packages);
119 Py_XDECREF(res);
120
121 return error_from_hr(hr);
122}
123
124static PyObject *find_all_instances()
125{
126 ISetupConfiguration *sc = nullptr;
127 ISetupConfiguration2 *sc2 = nullptr;
128 IEnumSetupInstances *enm = nullptr;
129 ISetupInstance *inst = nullptr;
130 ISetupInstance2 *inst2 = nullptr;
131 PyObject *res = nullptr;
132 ULONG fetched;
133 HRESULT hr;
134
135 if (!(res = PyList_New(0)))
136 goto error;
137
138 if (FAILED(hr = CoCreateInstance(
139 __uuidof(SetupConfiguration),
140 NULL,
141 CLSCTX_INPROC_SERVER,
142 __uuidof(ISetupConfiguration),
143 (LPVOID*)&sc
144 )) && hr != REGDB_E_CLASSNOTREG)
145 goto error;
146
147 // If the class is not registered, there are no VS instances installed
148 if (hr == REGDB_E_CLASSNOTREG)
149 return res;
150
151 if (FAILED(hr = sc->QueryInterface(&sc2)) ||
152 FAILED(hr = sc2->EnumAllInstances(&enm)))
153 goto error;
154
155 while (SUCCEEDED(enm->Next(1, &inst, &fetched)) && fetched) {
156 PyObject *name = nullptr;
157 PyObject *version = nullptr;
158 PyObject *path = nullptr;
159 PyObject *packages = nullptr;
Steve Doweraf8d6b92017-09-08 11:35:38 -0700160 PyObject *tuple = nullptr;
Steve Dower05f01d82017-09-07 11:49:23 -0700161
162 if (FAILED(hr = inst->QueryInterface(&inst2)) ||
163 !(name = get_install_name(inst2)) ||
164 !(version = get_install_version(inst)) ||
165 !(path = get_install_path(inst)) ||
166 !(packages = get_installed_packages(inst2)) ||
Steve Doweraf8d6b92017-09-08 11:35:38 -0700167 !(tuple = PyTuple_Pack(4, name, version, path, packages)) ||
168 PyList_Append(res, tuple) < 0)
Steve Dower05f01d82017-09-07 11:49:23 -0700169 goto iter_error;
170
Steve Doweraf8d6b92017-09-08 11:35:38 -0700171 Py_DECREF(tuple);
172 Py_DECREF(packages);
173 Py_DECREF(path);
174 Py_DECREF(version);
175 Py_DECREF(name);
Steve Dower05f01d82017-09-07 11:49:23 -0700176 continue;
177 iter_error:
178 if (inst2) inst2->Release();
Steve Doweraf8d6b92017-09-08 11:35:38 -0700179 Py_XDECREF(tuple);
Steve Dower05f01d82017-09-07 11:49:23 -0700180 Py_XDECREF(packages);
181 Py_XDECREF(path);
182 Py_XDECREF(version);
183 Py_XDECREF(name);
184 goto error;
185 }
186
187 enm->Release();
188 sc2->Release();
189 sc->Release();
190 return res;
191
192error:
193 if (enm) enm->Release();
194 if (sc2) sc2->Release();
195 if (sc) sc->Release();
196 Py_XDECREF(res);
197
198 return error_from_hr(hr);
199}
200
201PyDoc_STRVAR(findvs_findall_doc, "findall()\
202\
203Finds all installed versions of Visual Studio.\
204\
205This function will initialize COM temporarily. To avoid impact on other parts\
206of your application, use a new thread to make this call.");
207
208static PyObject *findvs_findall(PyObject *self, PyObject *args, PyObject *kwargs)
209{
210 HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
211 if (hr == RPC_E_CHANGED_MODE)
212 hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
213 if (FAILED(hr))
214 return error_from_hr(hr);
215 PyObject *res = find_all_instances();
216 CoUninitialize();
217 return res;
218}
219
220// List of functions to add to findvs in exec_findvs().
221static PyMethodDef findvs_functions[] = {
222 { "findall", (PyCFunction)findvs_findall, METH_VARARGS | METH_KEYWORDS, findvs_findall_doc },
223 { NULL, NULL, 0, NULL }
224};
225
226// Initialize findvs. May be called multiple times, so avoid
227// using static state.
228static int exec_findvs(PyObject *module)
229{
230 PyModule_AddFunctions(module, findvs_functions);
231
232 return 0; // success
233}
234
Steve Dower2507e292018-01-19 09:09:36 +1100235PyDoc_STRVAR(findvs_doc, "The _distutils_findvs helper module");
Steve Dower05f01d82017-09-07 11:49:23 -0700236
237static PyModuleDef_Slot findvs_slots[] = {
238 { Py_mod_exec, exec_findvs },
239 { 0, NULL }
240};
241
242static PyModuleDef findvs_def = {
243 PyModuleDef_HEAD_INIT,
Steve Dower2507e292018-01-19 09:09:36 +1100244 "_distutils_findvs",
Steve Dower05f01d82017-09-07 11:49:23 -0700245 findvs_doc,
246 0, // m_size
247 NULL, // m_methods
248 findvs_slots,
249 NULL, // m_traverse
250 NULL, // m_clear
251 NULL, // m_free
252};
253
254extern "C" {
Steve Dower2507e292018-01-19 09:09:36 +1100255 PyMODINIT_FUNC PyInit__distutils_findvs(void)
Steve Dower05f01d82017-09-07 11:49:23 -0700256 {
257 return PyModuleDef_Init(&findvs_def);
258 }
Steve Dower2507e292018-01-19 09:09:36 +1100259}