diff options
Diffstat (limited to 'PC/_findvs.cpp')
-rw-r--r-- | PC/_findvs.cpp | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/PC/_findvs.cpp b/PC/_findvs.cpp new file mode 100644 index 0000000..6c66011 --- /dev/null +++ b/PC/_findvs.cpp @@ -0,0 +1,255 @@ +// +// 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" +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "oleaut32.lib") +#pragma comment(lib, "version.lib") +#pragma comment(lib, "Microsoft.VisualStudio.Setup.Configuration.Native.lib") + +#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; + + 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)) || + PyList_Append(res, PyTuple_Pack(4, name, version, path, packages)) < 0) + goto iter_error; + + continue; + iter_error: + if (inst2) inst2->Release(); + 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 _findvs helper module"); + +static PyModuleDef_Slot findvs_slots[] = { + { Py_mod_exec, exec_findvs }, + { 0, NULL } +}; + +static PyModuleDef findvs_def = { + PyModuleDef_HEAD_INIT, + "_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__findvs(void) + { + return PyModuleDef_Init(&findvs_def); + } +}
\ No newline at end of file |