// // Helper library for location Visual Studio installations // using the COM-based query API. // // Copyright (c) Microsoft Corporation // Licensed to PSF under a contributor agreement // // Version history // 2017-05: Initial contribution (Steve Dower) #include #include #include "external\include\Setup.Configuration.h" #include static PyObject *error_from_hr(HRESULT hr) { if (FAILED(hr)) PyErr_Format(PyExc_OSError, "Error %08x", hr); assert(PyErr_Occurred()); return nullptr; } static PyObject *get_install_name(ISetupInstance2 *inst) { HRESULT hr; BSTR name; PyObject *str = nullptr; if (FAILED(hr = inst->GetDisplayName(LOCALE_USER_DEFAULT, &name))) goto error; str = PyUnicode_FromWideChar(name, SysStringLen(name)); SysFreeString(name); return str; error: return error_from_hr(hr); } static PyObject *get_install_version(ISetupInstance *inst) { HRESULT hr; BSTR ver; PyObject *str = nullptr; if (FAILED(hr = inst->GetInstallationVersion(&ver))) goto error; str = PyUnicode_FromWideChar(ver, SysStringLen(ver)); SysFreeString(ver); return str; error: return error_from_hr(hr); } static PyObject *get_install_path(ISetupInstance *inst) { HRESULT hr; BSTR path; PyObject *str = nullptr; if (FAILED(hr = inst->GetInstallationPath(&path))) goto error; str = PyUnicode_FromWideChar(path, SysStringLen(path)); SysFreeString(path); return str; error: return error_from_hr(hr); } static PyObject *get_installed_packages(ISetupInstance2 *inst) { HRESULT hr; PyObject *res = nullptr; LPSAFEARRAY sa_packages = nullptr; LONG ub = 0; IUnknown **packages = nullptr; PyObject *str = nullptr; if (FAILED(hr = inst->GetPackages(&sa_packages)) || FAILED(hr = SafeArrayAccessData(sa_packages, (void**)&packages)) || FAILED(SafeArrayGetUBound(sa_packages, 1, &ub)) || !(res = PyList_New(0))) goto error; for (LONG i = 0; i < ub; ++i) { ISetupPackageReference *package = nullptr; BSTR id = nullptr; PyObject *str = nullptr; if (FAILED(hr = packages[i]->QueryInterface(&package)) || FAILED(hr = package->GetId(&id))) goto iter_error; str = PyUnicode_FromWideChar(id, SysStringLen(id)); SysFreeString(id); if (!str || PyList_Append(res, str) < 0) goto iter_error; Py_CLEAR(str); package->Release(); continue; iter_error: if (package) package->Release(); Py_XDECREF(str); goto error; } SafeArrayUnaccessData(sa_packages); SafeArrayDestroy(sa_packages); return res; error: if (sa_packages && packages) SafeArrayUnaccessData(sa_packages); if (sa_packages) SafeArrayDestroy(sa_packages); Py_XDECREF(res); return error_from_hr(hr); } static PyObject *find_all_instances() { ISetupConfiguration *sc = nullptr; ISetupConfiguration2 *sc2 = nullptr; IEnumSetupInstances *enm = nullptr; ISetupInstance *inst = nullptr; ISetupInstance2 *inst2 = nullptr; PyObject *res = nullptr; ULONG fetched; HRESULT hr; if (!(res = PyList_New(0))) goto error; if (FAILED(hr = CoCreateInstance( __uuidof(SetupConfiguration), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISetupConfiguration), (LPVOID*)&sc )) && hr != REGDB_E_CLASSNOTREG) goto error; // If the class is not registered, there are no VS instances installed if (hr == REGDB_E_CLASSNOTREG) return res; if (FAILED(hr = sc->QueryInterface(&sc2)) || FAILED(hr = sc2->EnumAllInstances(&enm))) goto error; while (SUCCEEDED(enm->Next(1, &inst, &fetched)) && fetched) { PyObject *name = nullptr; PyObject *version = nullptr; PyObject *path = nullptr; PyObject *packages = nullptr; PyObject *tuple = nullptr; if (FAILED(hr = inst->QueryInterface(&inst2)) || !(name = get_install_name(inst2)) || !(version = get_install_version(inst)) || !(path = get_install_path(inst)) || !(packages = get_installed_packages(inst2)) || !(tuple = PyTuple_Pack(4, name, version, path, packages)) || PyList_Append(res, tuple) < 0) goto iter_error; Py_DECREF(tuple); Py_DECREF(packages); Py_DECREF(path); Py_DECREF(version); Py_DECREF(name); continue; iter_error: if (inst2) inst2->Release(); Py_XDECREF(tuple); Py_XDECREF(packages); Py_XDECREF(path); Py_XDECREF(version); Py_XDECREF(name); goto error; } enm->Release(); sc2->Release(); sc->Release(); return res; error: if (enm) enm->Release(); if (sc2) sc2->Release(); if (sc) sc->Release(); Py_XDECREF(res); return error_from_hr(hr); } PyDoc_STRVAR(findvs_findall_doc, "findall()\ \ Finds all installed versions of Visual Studio.\ \ This function will initialize COM temporarily. To avoid impact on other parts\ of your application, use a new thread to make this call."); static PyObject *findvs_findall(PyObject *self, PyObject *args, PyObject *kwargs) { HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if (hr == RPC_E_CHANGED_MODE) hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); if (FAILED(hr)) return error_from_hr(hr); PyObject *res = find_all_instances(); CoUninitialize(); return res; } // List of functions to add to findvs in exec_findvs(). static PyMethodDef findvs_functions[] = { { "findall", (PyCFunction)findvs_findall, METH_VARARGS | METH_KEYWORDS, findvs_findall_doc }, { NULL, NULL, 0, NULL } }; // Initialize findvs. May be called multiple times, so avoid // using static state. static int exec_findvs(PyObject *module) { PyModule_AddFunctions(module, findvs_functions); return 0; // success } PyDoc_STRVAR(findvs_doc, "The _distutils_findvs helper module"); static PyModuleDef_Slot findvs_slots[] = { { Py_mod_exec, exec_findvs }, { 0, NULL } }; static PyModuleDef findvs_def = { PyModuleDef_HEAD_INIT, "_distutils_findvs", findvs_doc, 0, // m_size NULL, // m_methods findvs_slots, NULL, // m_traverse NULL, // m_clear NULL, // m_free }; extern "C" { PyMODINIT_FUNC PyInit__distutils_findvs(void) { return PyModuleDef_Init(&findvs_def); } }