"""Downloads and extract the Visual C++ Redistributables. This is useful when working with minidump files as the user may be running with a new different version of the runtime. This script aims to maintain a copy of the various versions. Versions are normally added once I encoourter them. This requires dark.exe from Wix (http://wixtoolset.org/releases/) and expand.exe (File Expansion Utility - this comes with Microsoft Windows). """ from __future__ import print_function import os import requests import subprocess import tempfile import zipfile def download_file(uri, dest): """Download the uri to dest.""" if os.path.isfile(dest): # Skipping, already downloaded. return dest r = requests.get(uri, stream=True) with open(dest, 'wb') as f: for chunk in r.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks f.write(chunk) return dest def fetch_runtimes(download_directory): """Fetches the Visual C++ x64 Redistributable from Microsoft. download_directory: The directory to write the resulting files in. Return the (version, runtime_file) for each known runtime. """ # Some of these packages came from: # https://blogs.technet.microsoft.com/jagbal/2017/09/04/where-can-i-download-the-old-visual-c-redistributables/ # Others came from Choclately (https://chocolatey.org/) runtime_downloads = [ ( '14.0.23026.0', 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe', # This next one seems to be the same version. #'https://download.microsoft.com/download/4/7/a/47a9d485-9e3a-4707-be1d-26011ff15f42/vc_redist.x64.exe', ), ( '14.0.25325.0', 'https://download.visualstudio.microsoft.com/download/pr/11100230/15ccb3f02745c7b206ad10373cbca89b/VC_redist.x64.exe', ), ( '14.10.25017', 'https://download.microsoft.com/download/3/b/f/3bf6e759-c555-4595-8973-86b7b4312927/vc_redist.x64.exe', ), ( '14.10.25008.0', 'https://download.microsoft.com/download/8/9/d/89d195e1-1901-4036-9a75-fbe46443fc5a/vc_redist.x64.exe', ), ( '14.10.25017.0', 'https://download.microsoft.com/download/3/b/f/3bf6e759-c555-4595-8973-86b7b4312927/vc_redist.x64.exe', ), ( '14.11.25325.0', 'https://download.visualstudio.microsoft.com/download/pr/11100230/15ccb3f02745c7b206ad10373cbca89b/VC_redist.x64.exe', ), ( '14.12.25810', 'https://download.visualstudio.microsoft.com/download/pr/100349091/2cd2dba5748dc95950a5c42c2d2d78e4/VC_redist.x64.exe', ), ( '14.12.2581', 'https://download.visualstudio.microsoft.com/download/pr/100349091/2cd2dba5748dc95950a5c42c2d2d78e4/VC_redist.x64.exe', ), ] for version, download_uri in runtime_downloads: filename = version + '_' + os.path.basename(download_uri) destination = download_file(download_uri, os.path.join(download_directory, filename)) yield version, destination def fetch_wix(download_directory): """Downloads the WiX toolset. This is used to extract the runtime packages. """ zip_path = os.path.join(download_directory, '__wix.zip') if not os.path.isfile(zip_path): wix = 'https://github.com/wixtoolset/wix3/releases/download/' + \ 'wix3111rtm/wix311-binaries.zip' download_file(wix, zip_path) with zipfile.ZipFile(zip_path, 'r') as zip_file: zip_file.extractall(os.path.join(download_directory, '__wix')) return os.path.join(download_directory, '__wix') def extract_burn_bundle(wix_tool_location, bundle_exe): """Extracts a burn bundle to a temporary directory. bundle_exe: The bundle executable to extract. """ dark_exe = os.path.join(wix_tool_location, 'dark.exe') output_directory = tempfile.mkdtemp() subprocess.call([dark_exe, "-nologo", "-x", output_directory, bundle_exe]) return output_directory def find_cabs(directory): base_path = os.path.join(directory, 'AttachedContainer', 'packages') for name in os.listdir(base_path): if name.endswith('_amd64'): yield os.path.join(base_path, name, 'cab1.cab') def extract_cab(cab_source, destination): subprocess.call(['expand.exe', "-F:*", cab_source, destination]) def fetch_all(base_directory): download_directory = os.path.join(base_directory, 'Downloads') wix_tool_location = fetch_wix(download_directory) for ver, installer in fetch_runtimes(download_directory): output_directory = os.path.join(base_directory, 'vcruntime_' + ver) if not os.path.isdir(output_directory): os.makedirs(output_directory) if os.listdir(output_directory): print('Already have ' + ver) continue for cab in find_cabs(extract_burn_bundle(wix_tool_location, installer)): extract_cab(cab, output_directory) if __name__ == '__main__': fetch_all(r'd:\Microsoft\Runtimes')