├── .gitignore ├── LICENSE ├── README.md ├── vslocate.c └── vslocate.py /.gitignore: -------------------------------------------------------------------------------- 1 | .gitconfig 2 | *.pydevproject 3 | .project 4 | .metadata 5 | bin/ 6 | tmp/ 7 | *.tmp 8 | *.bak 9 | *.swp 10 | *~.nib 11 | local.properties 12 | .classpath 13 | .settings/ 14 | .loadpath 15 | .ninja* 16 | build.ninja 17 | 18 | # Generated version 19 | version.c 20 | 21 | # External tool builders 22 | .externalToolBuilders/ 23 | 24 | # Locally stored "Eclipse launch configurations" 25 | *.launch 26 | 27 | # CDT-specific 28 | .cproject 29 | 30 | # PDT-specific 31 | .buildpath 32 | 33 | #Android local build files 34 | build/android/assets 35 | build/android/libs 36 | 37 | #Xcode build 38 | build/xcode/foundation/build 39 | 40 | #Doxygen generated 41 | doc/html 42 | 43 | #Coverity scan 44 | cov-int/ 45 | cov-int.* 46 | 47 | ################# 48 | ## Visual Studio 49 | ################# 50 | 51 | ## Ignore Visual Studio temporary files, build results, and 52 | ## files generated by popular Visual Studio add-ons. 53 | 54 | # User-specific files 55 | *.suo 56 | *.user 57 | *.sln.docstates 58 | 59 | # Build results 60 | [Dd]ebug/ 61 | [Rr]elease/ 62 | [Pp]rofile/ 63 | [Dd]eploy/ 64 | *_i.c 65 | *_p.c 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.pch 70 | *.pchi 71 | *.pdb 72 | *.pgc 73 | *.pgd 74 | *.rsp 75 | *.sbr 76 | *.tlb 77 | *.tli 78 | *.tlh 79 | *.tmp 80 | *.vspscc 81 | .builds 82 | *.dotCover 83 | *.lastbuildstate 84 | *.unsuccessfulbuild 85 | *.opendb 86 | *.vc 87 | *.VC.db 88 | *.db-shm 89 | *.db-wal 90 | 91 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 92 | #packages/ 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opensdf 99 | *.sdf 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | 105 | # ReSharper is a .NET coding add-in 106 | _ReSharper* 107 | 108 | # Installshield output folder 109 | [Ee]xpress 110 | 111 | # DocProject is a documentation generator add-in 112 | DocProject/buildhelp/ 113 | DocProject/Help/*.HxT 114 | DocProject/Help/*.HxC 115 | DocProject/Help/*.hhc 116 | DocProject/Help/*.hhk 117 | DocProject/Help/*.hhp 118 | DocProject/Help/Html2 119 | DocProject/Help/html 120 | 121 | # Click-Once directory 122 | publish 123 | 124 | # Others 125 | [Bb]in 126 | [Oo]bj 127 | sql 128 | TestResults 129 | *.Cache 130 | ClientBin 131 | stylecop.* 132 | ~$* 133 | *.dbmdl 134 | Generated_Code #added for RIA/Silverlight projects 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | 142 | 143 | ############ 144 | ## Windows 145 | ############ 146 | 147 | # Windows image file caches 148 | Thumbs.db 149 | 150 | # Folder config file 151 | Desktop.ini 152 | 153 | 154 | ############# 155 | ## Python 156 | ############# 157 | 158 | *.py[co] 159 | 160 | # Packages 161 | *.egg 162 | *.egg-info 163 | dist 164 | eggs 165 | parts 166 | var 167 | sdist 168 | develop-eggs 169 | .installed.cfg 170 | 171 | # Installer logs 172 | pip-log.txt 173 | 174 | # Unit test / coverage reports 175 | .coverage 176 | .tox 177 | 178 | #Translations 179 | *.mo 180 | 181 | #Mr Developer 182 | .mr.developer.cfg 183 | 184 | # Mac crap 185 | .DS_Store 186 | 187 | 188 | ############### 189 | ## Generic 190 | ############### 191 | 192 | #Project builds 193 | lib/** 194 | bin/** 195 | dist/** 196 | libs/** 197 | 198 | #Log files 199 | *.log 200 | *.tlog 201 | 202 | #Scons build 203 | .sconsign.dblite 204 | build/scons/debug* 205 | build/scons/release* 206 | build/scons/profi* 207 | build/scons/deploy* 208 | 209 | #Backups 210 | *~ 211 | 212 | #Object files 213 | *.o 214 | 215 | #XCode 216 | xcuserdata 217 | *.xccheckout 218 | 219 | #SublimeText local workspace 220 | *.sublime-workspace 221 | 222 | #Never store keystores in version control! 223 | *.keystore 224 | 225 | #Do not store local build prefs 226 | build.json 227 | codesign.json 228 | coveralls.json 229 | coverallsreport.json 230 | codecov.json 231 | codecovreport.json 232 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Locate Visual Studio installations in Python and C 2 | 3 | Locating Visual Studio installations can be suprisingly hard. This repostitory provides basic implementations for doing this in Python and C, by using the Visual Studio Setup Configuration utility DLL installed by Visual Studio. The code is released to the public domain. 4 | 5 | ## Python 6 | 7 | Use the provided function to get a list of version and path tuples of all installed versions of Visual Studio 8 | 9 | ``` 10 | from vslocate import get_vs_installations 11 | 12 | for version, path in get_vs_installations(): 13 | print(version + " " + path) 14 | ``` 15 | 16 | or run the script directly 17 | 18 | ``` 19 | python vslocate.py 20 | ``` 21 | 22 | The output is one line per installation with version number and path, for example 23 | 24 | ``` 25 | 15.9.28307.586 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise 26 | 16.0.28729.10 C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise 27 | ``` 28 | 29 | ## C 30 | 31 | Use the enumeration function to get a callback for each installed version of Visual Studio 32 | 33 | ``` 34 | #include 35 | 36 | void 37 | my_callback(const wchar_t* version, const wchar_t* path) { 38 | printf("%ls %ls\n", version, path); 39 | } 40 | 41 | int 42 | main(int argc, char** argv) { 43 | (void)sizeof(argc); 44 | (void)sizeof(argv); 45 | 46 | return get_vs_installations(my_callback); 47 | } 48 | ``` 49 | 50 | Or, to compile the C tool use 51 | 52 | ``` 53 | cl /O1 /Os /Gm- /GF- /sdl /MT /GS /D COMPILE_TOOL=1 vslocate.c /link /out:vslocate.exe 54 | ``` 55 | 56 | or 57 | 58 | ``` 59 | clang -Oz -DCOMPILE_TOOL=1 vslocate.c -o vslocate.exe 60 | ``` 61 | 62 | The output of the tool is one line per installation with version number and path, for example 63 | 64 | ``` 65 | 15.9.28307.586 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise 66 | 16.0.28729.10 C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise 67 | ``` 68 | -------------------------------------------------------------------------------- /vslocate.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | 7 | // Callback for receiving installation instances 8 | typedef void (*instance_callback)(const wchar_t* version, const wchar_t* path); 9 | 10 | extern int 11 | get_vs_installations(instance_callback callback); 12 | 13 | 14 | typedef struct ISetupInstanceVTable ISetupInstanceVTable; 15 | typedef struct ISetupInstance ISetupInstance; 16 | 17 | typedef struct IEnumSetupInstancesVTable IEnumSetupInstancesVTable; 18 | typedef struct IEnumSetupInstances IEnumSetupInstances; 19 | 20 | typedef struct ISetupConfigurationVTable ISetupConfigurationVTable; 21 | typedef struct ISetupConfiguration ISetupConfiguration; 22 | 23 | typedef HRESULT (__stdcall* QueryInterfaceFn)(void* this, REFIID riid, void** object); 24 | typedef ULONG (__stdcall* AddRefFn)(void* this); 25 | typedef ULONG (__stdcall* ReleaseFn)(void* this); 26 | 27 | #define DECLARE_IUNKNOWN \ 28 | QueryInterfaceFn QueryInterface; \ 29 | AddRefFn AddRef; \ 30 | ReleaseFn Release 31 | 32 | typedef HRESULT (__stdcall* GetInstanceIdFn)( 33 | ISetupInstance* instance, 34 | wchar_t** id); 35 | 36 | typedef HRESULT (__stdcall* GetInstallDateFn)( 37 | ISetupInstance* instance, 38 | LPFILETIME date); 39 | 40 | typedef HRESULT (__stdcall* GetInstallationNameFn)( 41 | ISetupInstance* instance, 42 | wchar_t** name); 43 | 44 | typedef HRESULT (__stdcall* GetInstallationPathFn)( 45 | ISetupInstance* instance, 46 | wchar_t** path); 47 | 48 | typedef HRESULT (__stdcall* GetInstallationVersionFn)( 49 | ISetupInstance* instance, 50 | wchar_t** version); 51 | 52 | typedef HRESULT (__stdcall* GetDisplayNameFn)( 53 | ISetupInstance* instance, 54 | LCID id, 55 | wchar_t** name); 56 | 57 | typedef HRESULT (__stdcall* GetDescriptionFn)( 58 | ISetupInstance* instance, 59 | LCID id, 60 | wchar_t** description); 61 | 62 | typedef HRESULT (__stdcall* ResolvePathFn)( 63 | ISetupInstance* instance, 64 | const wchar_t* relative_path, 65 | wchar_t** absolute_path); 66 | 67 | struct ISetupInstanceVTable { 68 | DECLARE_IUNKNOWN; 69 | 70 | GetInstanceIdFn GetInstanceId; 71 | GetInstallDateFn GetInstallDate; 72 | GetInstallationNameFn GetInstallationName; 73 | GetInstallationPathFn GetInstallationPath; 74 | GetInstallationVersionFn GetInstallationVersion; 75 | GetDisplayNameFn GetDisplayName; 76 | GetDescriptionFn GetDescription; 77 | ResolvePathFn ResolvePath; 78 | }; 79 | 80 | struct ISetupInstance { 81 | ISetupInstanceVTable* vtable; 82 | }; 83 | 84 | typedef HRESULT (__stdcall* NextFn)( 85 | IEnumSetupInstances* instance, 86 | ULONG num, 87 | ISetupInstance** setup, 88 | ULONG* fetched); 89 | 90 | typedef HRESULT (__stdcall* SkipFn)( 91 | IEnumSetupInstances* instance, 92 | ULONG num); 93 | 94 | typedef HRESULT (__stdcall* ResetFn)( 95 | IEnumSetupInstances* instance); 96 | 97 | typedef HRESULT (__stdcall* CloneFn)( 98 | IEnumSetupInstances* instance, 99 | IEnumSetupInstances** instances 100 | ); 101 | 102 | struct IEnumSetupInstancesVTable { 103 | DECLARE_IUNKNOWN; 104 | 105 | NextFn Next; 106 | SkipFn Skip; 107 | ResetFn Reset; 108 | CloneFn Clone; 109 | }; 110 | 111 | struct IEnumSetupInstances { 112 | IEnumSetupInstancesVTable* vtable; 113 | }; 114 | 115 | typedef HRESULT(__stdcall* EnumInstancesFn)( 116 | ISetupConfiguration* configuration, 117 | IEnumSetupInstances** instances); 118 | 119 | struct ISetupConfigurationVTable { 120 | DECLARE_IUNKNOWN; 121 | 122 | EnumInstancesFn EnumInstances; 123 | void* GetInstanceForCurrentProcess; 124 | void* GetInstanceForPath; 125 | }; 126 | 127 | typedef struct ISetupConfiguration { 128 | ISetupConfigurationVTable* vtable; 129 | } ISetupConfiguration; 130 | 131 | typedef HRESULT(__stdcall* GetSetupConfigurationFn)( 132 | ISetupConfiguration** configuration, 133 | void* reserved); 134 | 135 | static size_t 136 | environment_variable(const char* variable, char* value, size_t capacity) { 137 | unsigned int required; 138 | if ((required = GetEnvironmentVariableA(variable, value, (unsigned int)capacity)) > capacity) 139 | return 0; 140 | return (required > 0) ? required : 0; 141 | } 142 | 143 | static size_t 144 | get_library_path(char* path, size_t capacity) { 145 | #if defined( __x86_64__ ) || defined( __x86_64 ) || defined( __amd64 ) || defined( _M_AMD64 ) || defined( _AMD64_ ) 146 | const char subpath[] = "\\Microsoft\\VisualStudio\\Setup\\x64\\Microsoft.VisualStudio.Setup.Configuration.Native.dll\0"; 147 | #else 148 | const char subpath[] = "\\Microsoft\\VisualStudio\\Setup\\x86\\Microsoft.VisualStudio.Setup.Configuration.Native.dll\0"; 149 | #endif 150 | size_t path_length = environment_variable("ProgramData", path, capacity); 151 | size_t subpath_length = sizeof(subpath); 152 | if (!path_length || (path_length + subpath_length) > capacity) 153 | return 0; 154 | 155 | memcpy(path + path_length, subpath, subpath_length); 156 | return path_length + subpath_length; 157 | } 158 | 159 | 160 | int 161 | get_vs_installations(instance_callback callback) { 162 | HMODULE lib = 0; 163 | int result = -1; 164 | 165 | char lib_path[512]; 166 | size_t path_length = get_library_path(lib_path, sizeof(lib_path)); 167 | if (!path_length) { 168 | printf("ERROR: Unable to get VisualStudio Setup Configuration library path\n"); 169 | goto cleanup; 170 | } 171 | 172 | lib = LoadLibraryA(lib_path); 173 | if (!lib) { 174 | printf("ERROR: Unable to load VisualStudio Setup Configuration library\n"); 175 | goto cleanup; 176 | } 177 | 178 | GetSetupConfigurationFn get_setup_configuration = (GetSetupConfigurationFn)GetProcAddress(lib, "GetSetupConfiguration"); 179 | if (!get_setup_configuration) { 180 | printf("ERROR: Unable to get VisualStudio Setup Configuration entry point\n"); 181 | goto cleanup; 182 | } 183 | 184 | ISetupConfiguration* configuration = 0; 185 | HRESULT code = get_setup_configuration(&configuration, 0); 186 | if (code != S_OK) { 187 | printf("ERROR: GetSetupConfiguration call failed (0x%08lx)\n", code); 188 | goto cleanup; 189 | } 190 | 191 | IEnumSetupInstances* enum_instances = 0; 192 | code = configuration->vtable->EnumInstances(configuration, &enum_instances); 193 | if (code != S_OK) { 194 | printf("ERROR: EnumInstances call failed (0x%08lx)\n", code); 195 | goto cleanup; 196 | } 197 | 198 | result = 0; 199 | while (enum_instances) { 200 | ULONG fetched = 0; 201 | ISetupInstance* setup_instance = 0; 202 | code = enum_instances->vtable->Next(enum_instances, 1, &setup_instance, &fetched); 203 | if ((code == S_FALSE) || !fetched) 204 | break; 205 | if (code != S_OK) { 206 | printf("ERROR: While enumerating instances, Next call failed (0x%08lx)\n", code); 207 | goto cleanup; 208 | } 209 | 210 | wchar_t* version = 0; 211 | wchar_t* path = 0; 212 | code = setup_instance->vtable->GetInstallationVersion(setup_instance, &version); 213 | if (code == S_OK) 214 | code = setup_instance->vtable->GetInstallationPath(setup_instance, &path); 215 | if (code == S_OK) 216 | printf("%ls %ls\n", version, path); 217 | } 218 | 219 | cleanup: 220 | 221 | if (lib) 222 | FreeLibrary(lib); 223 | 224 | return result; 225 | } 226 | 227 | #ifdef COMPILE_TOOL 228 | 229 | static void 230 | print_callback(const wchar_t* version, const wchar_t* path) { 231 | printf("%ls %ls\n", version, path); 232 | } 233 | 234 | int 235 | main(int argc, char** argv) { 236 | (void)sizeof(argc); 237 | (void)sizeof(argv); 238 | 239 | return get_vs_installations(print_callback); 240 | } 241 | 242 | #endif 243 | -------------------------------------------------------------------------------- /vslocate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Locate Visual Studio installations with Visual Studio Setup Configuration utility DLL""" 4 | 5 | import os 6 | import ctypes 7 | 8 | def get_vs_installations(): 9 | 10 | class ISetupInstanceVTable(ctypes.Structure): 11 | """Class matching VisualStudio Setup package ISetupInstance vtable""" 12 | pass 13 | 14 | class ISetupInstance(ctypes.Structure): 15 | """COM interface for ISetupInstance""" 16 | _fields_ = [('vtable', ctypes.POINTER(ISetupInstanceVTable))] 17 | 18 | class IEnumSetupInstancesVTable(ctypes.Structure): 19 | """Class matching VisualStudio Setup package IEnumSetupInstances vtable""" 20 | pass 21 | 22 | class IEnumSetupInstances(ctypes.Structure): 23 | """COM interface for IEnumSetupInstances""" 24 | _fields_ = [('vtable', ctypes.POINTER(IEnumSetupInstancesVTable))] 25 | 26 | class ISetupConfigurationVTable(ctypes.Structure): 27 | """Class matching VisualStudio Setup package ISetupConfiguration vtable""" 28 | pass 29 | 30 | class ISetupConfiguration(ctypes.Structure): 31 | """COM interface for ISetupConfiguration""" 32 | _fields_ = [('vtable', ctypes.POINTER(ISetupConfigurationVTable))] 33 | 34 | proto_get_installation_path = ctypes.WINFUNCTYPE( 35 | ctypes.c_int, 36 | ctypes.POINTER(ISetupInstance), 37 | ctypes.POINTER(ctypes.c_wchar_p)) 38 | 39 | proto_get_installation_version = ctypes.WINFUNCTYPE( 40 | ctypes.c_int, 41 | ctypes.POINTER(ISetupInstance), 42 | ctypes.POINTER(ctypes.c_wchar_p)) 43 | 44 | ISetupInstanceVTable._fields_ = ( 45 | ('QueryInterface', ctypes.c_void_p), 46 | ('AddRef', ctypes.c_void_p), 47 | ('Release', ctypes.c_void_p), 48 | ('GetInstanceId', ctypes.c_void_p), 49 | ('GetInstallDate', ctypes.c_void_p), 50 | ('GetInstallationName', ctypes.c_void_p), 51 | ('GetInstallationPath', proto_get_installation_path), 52 | ('GetInstallationVersion', proto_get_installation_version), 53 | ('GetDisplayName', ctypes.c_void_p), 54 | ('GetDescription', ctypes.c_void_p), 55 | ('ResolvePath', ctypes.c_void_p)) 56 | 57 | proto_next = ctypes.WINFUNCTYPE( 58 | ctypes.c_int, 59 | ctypes.POINTER(IEnumSetupInstances), 60 | ctypes.c_int, 61 | ctypes.POINTER(ctypes.POINTER(ISetupInstance)), 62 | ctypes.POINTER(ctypes.c_int)) 63 | 64 | IEnumSetupInstancesVTable._fields_ = ( 65 | ('QueryInterface', ctypes.c_void_p), 66 | ('AddRef', ctypes.c_void_p), 67 | ('Release', ctypes.c_void_p), 68 | ('Next', proto_next), 69 | ('Skip', ctypes.c_void_p), 70 | ('Reset', ctypes.c_void_p), 71 | ('Clone', ctypes.c_void_p)) 72 | 73 | proto_enum_instances = ctypes.WINFUNCTYPE( 74 | ctypes.c_int, 75 | ctypes.POINTER(ISetupConfiguration), 76 | ctypes.POINTER(ctypes.POINTER(IEnumSetupInstances))) 77 | 78 | ISetupConfigurationVTable._fields_ = ( 79 | ('QueryInterface', ctypes.c_void_p), 80 | ('AddRef', ctypes.c_void_p), 81 | ('Release', ctypes.c_void_p), 82 | ('EnumInstances', proto_enum_instances), 83 | ('GetInstanceForCurrentProcess', ctypes.c_void_p), 84 | ('GetInstanceForPath', ctypes.c_void_p)) 85 | 86 | proto_get_setup_configuration = ctypes.WINFUNCTYPE( 87 | ctypes.c_int, 88 | ctypes.POINTER(ctypes.POINTER(ISetupConfiguration)), 89 | ctypes.c_void_p) 90 | 91 | installations = [] 92 | dll = None 93 | 94 | dll_path = os.path.expandvars("$ProgramData\\Microsoft\\VisualStudio\\Setup\\x64\\Microsoft.VisualStudio.Setup.Configuration.Native.dll") 95 | try: 96 | dll = ctypes.WinDLL(dll_path) 97 | except OSError as e: 98 | #print("Failed to load Visual Studio setup configuration DLL: " + str(e)) 99 | return installations 100 | 101 | params_get_setup_configuration = (1, "configuration", 0), (1, "reserved", 0), 102 | 103 | get_setup_configuration = proto_get_setup_configuration(("GetSetupConfiguration", dll), params_get_setup_configuration) 104 | 105 | configuration = ctypes.POINTER(ISetupConfiguration)() 106 | reserved = ctypes.c_void_p(0) 107 | 108 | result = get_setup_configuration(ctypes.byref(configuration), reserved) 109 | if result != 0: 110 | #print("Failed to get setup configuration: " + str(result)) 111 | return installations 112 | 113 | enum_instances = configuration.contents.vtable.contents.EnumInstances 114 | 115 | enum_setup_instances = ctypes.POINTER(IEnumSetupInstances)() 116 | result = enum_instances(configuration, ctypes.byref(enum_setup_instances)) 117 | if result != 0: 118 | #print("Failed to enum setup instances: " + str(result)) 119 | return installations 120 | 121 | 122 | setup_instance = ctypes.POINTER(ISetupInstance)() 123 | fetched = ctypes.c_int(0) 124 | 125 | while True: 126 | next = enum_setup_instances.contents.vtable.contents.Next 127 | result = next(enum_setup_instances, 1, ctypes.byref(setup_instance), ctypes.byref(fetched)) 128 | if result == 1 or fetched == 0: 129 | break 130 | if result != 0: 131 | #print("Failed to get next setup instance: " + str(result)) 132 | break 133 | 134 | version = ctypes.c_wchar_p() 135 | path = ctypes.c_wchar_p() 136 | 137 | get_installation_version = setup_instance.contents.vtable.contents.GetInstallationVersion 138 | get_installation_path = setup_instance.contents.vtable.contents.GetInstallationPath 139 | 140 | result = get_installation_version(setup_instance, ctypes.byref(version)) 141 | if result != 0: 142 | #print("Failed to get setup instance version: " + str(result)) 143 | break 144 | 145 | result = get_installation_path(setup_instance, ctypes.byref(path)) 146 | if result != 0: 147 | #print("Failed to get setup instance version: " + str(result)) 148 | break 149 | 150 | installations.append((version.value, path.value)) 151 | 152 | return installations 153 | 154 | 155 | if __name__ == "__main__": 156 | 157 | installations = get_vs_installations() 158 | 159 | for version, path in installations: 160 | print(version + " " + path) 161 | --------------------------------------------------------------------------------