|
|
//
// 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 <Windows.h>
#include <Strsafe.h>
#include "external\include\Setup.Configuration.h"
#include <Python.h>
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); }}
|