2017-09-07 15:49:23 -03:00
|
|
|
//
|
|
|
|
// 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;
|
2017-09-08 15:35:38 -03:00
|
|
|
PyObject *tuple = nullptr;
|
2017-09-07 15:49:23 -03:00
|
|
|
|
|
|
|
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)) ||
|
2017-09-08 15:35:38 -03:00
|
|
|
!(tuple = PyTuple_Pack(4, name, version, path, packages)) ||
|
|
|
|
PyList_Append(res, tuple) < 0)
|
2017-09-07 15:49:23 -03:00
|
|
|
goto iter_error;
|
|
|
|
|
2017-09-08 15:35:38 -03:00
|
|
|
Py_DECREF(tuple);
|
|
|
|
Py_DECREF(packages);
|
|
|
|
Py_DECREF(path);
|
|
|
|
Py_DECREF(version);
|
|
|
|
Py_DECREF(name);
|
2017-09-07 15:49:23 -03:00
|
|
|
continue;
|
|
|
|
iter_error:
|
|
|
|
if (inst2) inst2->Release();
|
2017-09-08 15:35:38 -03:00
|
|
|
Py_XDECREF(tuple);
|
2017-09-07 15:49:23 -03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|