mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 19:34:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			263 lines
		
	
	
		
			No EOL
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			No EOL
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//
 | 
						|
// 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;
 | 
						|
        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 _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);
 | 
						|
    }
 | 
						|
} |