├── .gitattributes ├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── CONTRIBUTING.md ├── DataFiles ├── Microsoft Visual Studio Community 2013 with Update 5.bin ├── Microsoft Visual Studio Community 2015.bin ├── Microsoft Visual Studio Enterprise 2015.bin ├── Microsoft Visual Studio Premium 2012.bin ├── Microsoft Visual Studio Premium 2013 with Update 5.bin ├── Microsoft Visual Studio Professional 2012.bin ├── Microsoft Visual Studio Professional 2013 with Update 4.bin ├── Microsoft Visual Studio Professional 2013 with Update 5.bin ├── Microsoft Visual Studio Professional 2015.bin ├── Microsoft Visual Studio Ultimate 2013 with Update 5.bin └── Microsoft Visual Studio Ultimate 2013.bin ├── ReadMe.md ├── TotalUninstaller EULA.docx ├── Uninstaller.sln ├── UninstallerTests ├── ConfigurationManagerSupportTests.cs ├── ConfigurationManagerTests.cs ├── Properties │ └── AssemblyInfo.cs ├── Uninstall_WrapperTests.cs └── UninstallerTests.csproj ├── lib ├── Branding.rc ├── Branding.targets ├── Common.Cpp.props ├── Common.NuGet.targets ├── Common.csproj.props ├── Common.csproj.targets ├── Common.props ├── Common.ruleset ├── Common.targets └── Common.vcxproj.targets ├── license.txt ├── src ├── Uninstall_Wrapper │ ├── App.config │ ├── CommandOption.cs │ ├── ConsoleOperations.cs │ ├── DataFile.bin │ ├── Program.cs │ ├── TotalUninstaller EULA.docx │ ├── Uninstall_Wrapper.csproj │ ├── VisualStudioSpecifc.cs │ ├── app.manifest │ └── packages.config ├── VS.ConfigurationManager.Support │ ├── ArchitectureConfiguration.cs │ ├── ConfigurationManagerException.cs │ ├── ElevationDetection.cs │ ├── Logger.cs │ ├── NativeMethods.cs │ ├── OperatingSystemConfiguration.cs │ ├── RegistryHandler.cs │ ├── Utility.cs │ ├── VS.ConfigurationManager.Support.csproj │ └── packages.config └── VS.ConfigurationManager │ ├── Bundle.cs │ ├── BundlesAndPackagesStore.cs │ ├── External │ ├── Microsoft.Deployment.WindowsInstaller.dll │ └── wix.dll │ ├── Filter.cs │ ├── Package.cs │ ├── Primitives.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── SystemSettings.cs │ ├── UninstallAction.cs │ ├── VS.ConfigurationManager.csproj │ └── packages.config └── tools ├── PreBuild.ps1 └── Publish.proj /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | 163 | # RIA/Silverlight projects 164 | Generated_Code/ 165 | 166 | # Backup & report files from converting an old project file 167 | # to a newer Visual Studio version. Backup files are not needed, 168 | # because we have git ;-) 169 | _UpgradeReport_Files/ 170 | Backup*/ 171 | UpgradeLog*.XML 172 | UpgradeLog*.htm 173 | 174 | # SQL Server files 175 | *.mdf 176 | *.ldf 177 | 178 | # Business Intelligence projects 179 | *.rdl.data 180 | *.bim.layout 181 | *.bim_*.settings 182 | 183 | # Microsoft Fakes 184 | FakesAssemblies/ 185 | 186 | # LightSwitch generated files 187 | GeneratedArtifacts/ 188 | _Pvt_Extensions/ 189 | ModelManifest.xml 190 | 191 | # Original shared repo content 192 | telemetry/ 193 | tools/*/ 194 | -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/.nuget/NuGet.exe -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config 41 | 42 | 43 | 44 | $(MSBuildProjectDirectory)\packages.config 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | $(NuGetToolsPath)\NuGet.exe 51 | @(PackageSource) 52 | 53 | "$(NuGetExePath)" 54 | mono --runtime=v4.0.30319 "$(NuGetExePath)" 55 | 56 | $(TargetDir.Trim('\\')) 57 | 58 | -RequireConsent 59 | -NonInteractive 60 | 61 | "$(SolutionDir) " 62 | "$(SolutionDir)" 63 | 64 | 65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 67 | 68 | 69 | 70 | RestorePackages; 71 | $(BuildDependsOn); 72 | 73 | 74 | 75 | 76 | $(BuildDependsOn); 77 | BuildPackage; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute to the Microsoft Visual Studio Uninstaller 2 | 3 | In order to contribute, you will need to be able to build the source, deploy to test and run automated tests. 4 | Please fork and send pull requests. 5 | 6 | ## Building From Source 7 | 8 | ### Clone the repo 9 | ```bash 10 | git clone https://github.com/Microsoft/VisualStudioUninstaller.git 11 | ``` 12 | 13 | ### Build Pre-reqs 14 | 15 | You will need Visual Studio 2013 Community or greater in order to build this project. 16 | ```bash 17 | Download it for free here: https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx 18 | ``` 19 | 20 | ### Build 21 | Open Uninstaller.sln in Visual Studio 22 | ```bash 23 | Build Solution 24 | ``` 25 | 26 | This builds the project and output binaries to ./bin/Debug 27 | 28 | ### Run Tests 29 | 30 | Please run all unit tests in the solution prior to PR. 31 | 32 | ```bash 33 | Unit tests 34 | ``` 35 | -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Community 2013 with Update 5.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Community 2013 with Update 5.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Community 2015.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Community 2015.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Enterprise 2015.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Enterprise 2015.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Premium 2012.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Premium 2012.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Premium 2013 with Update 5.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Premium 2013 with Update 5.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Professional 2012.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Professional 2012.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Professional 2013 with Update 4.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Professional 2013 with Update 4.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Professional 2013 with Update 5.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Professional 2013 with Update 5.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Professional 2015.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Professional 2015.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Ultimate 2013 with Update 5.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Ultimate 2013 with Update 5.bin -------------------------------------------------------------------------------- /DataFiles/Microsoft Visual Studio Ultimate 2013.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/DataFiles/Microsoft Visual Studio Ultimate 2013.bin -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | Visual Studio Uninstaller 2 | ========= 3 | 4 | This executable is designed to clean up and delete Preview, RC and final releases of Visual Studio 2013 and Visual Studio 2015. It is designed to be used as a final resort to clean up a system of remaining artifacts from a non-successful installation, instead of having to reimage the machine. 5 | 6 | WARNING: running this application may stop earlier remaining installations of Visual Studio 2012 and earlier from working, because Visual Studio 2012 and below share MSI upgrade code with Visual Studio 2013 and above. 7 | 8 | Download: https://github.com/Microsoft/VisualStudioUninstaller/releases 9 | 10 | How it works? 11 | ======== 12 | 13 | This app finds and uninstall every Preview/RC/RTM release of Visual Studio 2013 and 2015. It will first execute uninstall command on the bundle, and then it will uninstall any stale MSIs. The application contains a master list of Bundle IDs and upgrade codes for every MSI ever chained in by Visual Studio 2013-2015. It will not uninstall MSU or MSIs that marked as ReallyPermanent. 14 | 15 | Status 16 | ======== 17 | Shipped 18 | 19 | Contributing and building this project 20 | ======== 21 | See CONTRIBUTING.md 22 | 23 | Goals/Vision/Scope 24 | ======== 25 | Our goal is to provide a way to thoroughly and reliably remove Visual Studio. This program first attempts to force uninstall Visual Studio from top down, and then remove any remaining MSIs and MSUs. This program will work on any BURN based Visual Studio; that means this program is only capable of removing Visual Studio 2012 - 2015. 26 | 27 | Mailing list/contacts/forums 28 | ======== 29 | https://www.visualstudio.com/support/support-overview-vs 30 | 31 | Usage 32 | ======== 33 | 34 | **How to debug Total Uninstaller remotely?** 35 | 36 | IMPORTANT: Do not run this on your development machine without setting the `DoNotExecuteProcess` flag. This will prevent the application from uninstalling the very development environment you are working from. 37 | 38 | To get the most out of the debug experience, I recommend the following: 39 | 40 | 1. Create a VM with Dev14 installed. 41 | 2. Start the 64-bit remote debugger with administrative privileges. 42 | 3. Copy the debug Bin directory to the VM. 43 | 4. Run the application with Administrative privileges. 44 | 5. Create a snapshot of the machine using Hyper-V. 45 | 6. Start a remote debugging session to your VM and attach. 46 | 7. Step through to your hearts delight. 47 | 8. If you find something you don’t like, restore the snapshot and recopy the Bin directory and go to step 6 again. 48 | 49 | **Using Total Uninstall:** 50 | 51 | 1. Download and unzip the zip file to a folder. 52 | 2. Open cmd.exe with Administrative privileges 53 | 2. Execute Setup.ForcedUninstall.exe 54 | 3. Press Y and hit enter to run the application. 55 | 4. If the application ask to reboot the system, please reboot the system, and rerun this application again. 56 | 57 | **Commands:** 58 | 59 | 1. help or /help or /? : Print usage. 60 | 2. break : run the application and pause until the user hit any key. 61 | 3. noprocess : run the application but does not uninstall anything. 62 | 63 | Roadmap 64 | ======== 65 | 1. Periodically update of the Total Uninstaller to ensure the data used for uninstallation is up to date with the most recent Visual Studio releases. 66 | 67 | Open issues 68 | ======== 69 | Please file an issue request as necessary. 70 | 71 | Guidelines 72 | ========= 73 | These are general guidelines for source code within this solution: 74 | 75 | Native code 76 | ----------- 77 | 78 | * Parameters should be declared with SAL annotation. 79 | * Input string parameters should be declared as LPCWSTR. 80 | * Output string parameters should be declared as CStringW& references. 81 | * Class members should not be references or pointers, but be created and destroyed with the owning class. 82 | 83 | 84 | Managed code 85 | ----------- 86 | 87 | Please follow these coding standards: 88 | https://msdn.microsoft.com/en-us/library/Ff926074.aspx?f=255&MSPPError=-2147217396 89 | -------------------------------------------------------------------------------- /TotalUninstaller EULA.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/TotalUninstaller EULA.docx -------------------------------------------------------------------------------- /Uninstaller.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uninstall_Wrapper", "src\Uninstall_Wrapper\Uninstall_Wrapper.csproj", "{1E70C6F2-8570-4620-822E-E3F71BF1A0F7}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VS.ConfigurationManager", "src\VS.ConfigurationManager\VS.ConfigurationManager.csproj", "{CACCC6E1-FCB8-4DBE-9159-0F8EAA69D27A}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VS.ConfigurationManager.Support", "src\VS.ConfigurationManager.Support\VS.ConfigurationManager.Support.csproj", "{13C73873-A5ED-42DE-97F0-A3B2D7A1D76F}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UninstallerTests", "UninstallerTests\UninstallerTests.csproj", "{9727E23C-496A-4887-9C87-C5A96F3B05B1}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {1E70C6F2-8570-4620-822E-E3F71BF1A0F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {1E70C6F2-8570-4620-822E-E3F71BF1A0F7}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {1E70C6F2-8570-4620-822E-E3F71BF1A0F7}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {1E70C6F2-8570-4620-822E-E3F71BF1A0F7}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {CACCC6E1-FCB8-4DBE-9159-0F8EAA69D27A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {CACCC6E1-FCB8-4DBE-9159-0F8EAA69D27A}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {CACCC6E1-FCB8-4DBE-9159-0F8EAA69D27A}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {CACCC6E1-FCB8-4DBE-9159-0F8EAA69D27A}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {13C73873-A5ED-42DE-97F0-A3B2D7A1D76F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {13C73873-A5ED-42DE-97F0-A3B2D7A1D76F}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {13C73873-A5ED-42DE-97F0-A3B2D7A1D76F}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {13C73873-A5ED-42DE-97F0-A3B2D7A1D76F}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {9727E23C-496A-4887-9C87-C5A96F3B05B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {9727E23C-496A-4887-9C87-C5A96F3B05B1}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {9727E23C-496A-4887-9C87-C5A96F3B05B1}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {9727E23C-496A-4887-9C87-C5A96F3B05B1}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /UninstallerTests/ConfigurationManagerSupportTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Microsoft.VS.ConfigurationManager.Support; 6 | 7 | namespace UninstallerTests 8 | { 9 | /// 10 | /// Summary description for ConfigurationManagerSupportTestscs 11 | /// 12 | [TestClass] 13 | public class ConfigurationManagerSupportTests 14 | { 15 | public ConfigurationManagerSupportTests() 16 | { 17 | // 18 | // TODO: Add constructor logic here 19 | // 20 | } 21 | 22 | private TestContext testContextInstance; 23 | 24 | /// 25 | ///Gets or sets the test context which provides 26 | ///information about and functionality for the current test run. 27 | /// 28 | public TestContext TestContext 29 | { 30 | get 31 | { 32 | return testContextInstance; 33 | } 34 | set 35 | { 36 | testContextInstance = value; 37 | } 38 | } 39 | 40 | #region Additional test attributes 41 | // 42 | // You can use the following additional attributes as you write your tests: 43 | // 44 | // Use ClassInitialize to run code before running the first test in the class 45 | // [ClassInitialize()] 46 | // public static void MyClassInitialize(TestContext testContext) { } 47 | // 48 | // Use ClassCleanup to run code after all tests in a class have run 49 | // [ClassCleanup()] 50 | // public static void MyClassCleanup() { } 51 | // 52 | // Use TestInitialize to run code before running each test 53 | // [TestInitialize()] 54 | // public void MyTestInitialize() { } 55 | // 56 | // Use TestCleanup to run code after each test has run 57 | // [TestCleanup()] 58 | // public void MyTestCleanup() { } 59 | // 60 | #endregion 61 | 62 | [TestMethod] 63 | public void LoggerTest() 64 | { 65 | Logger.LogLocation = null; 66 | Assert.IsTrue(Logger.LogLocation.StartsWith(System.IO.Path.GetTempPath())); 67 | Assert.IsTrue(Logger.LogLocation.EndsWith(".LOG", StringComparison.OrdinalIgnoreCase)); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /UninstallerTests/ConfigurationManagerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Microsoft.VS.ConfigurationManager; 4 | 5 | namespace UninstallerTests 6 | { 7 | [TestClass] 8 | public class ConfigurationManagerTests 9 | { 10 | [TestMethod] 11 | public void BundleConstructorTest() 12 | { 13 | Bundle bundle = new Bundle(); 14 | Assert.IsTrue(bundle.BundleId == Guid.Empty); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UninstallerTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UninstallerTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UninstallerTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("bfdfc571-75be-4727-9696-e1f7cff3edd6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /UninstallerTests/Uninstall_WrapperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace UninstallerTests 7 | { 8 | /// 9 | /// Summary description for Uninstall_Wrapper 10 | /// 11 | [TestClass] 12 | public class Uninstall_WrapperTests 13 | { 14 | public Uninstall_WrapperTests() 15 | { 16 | // 17 | // TODO: Add constructor logic here 18 | // 19 | } 20 | 21 | private TestContext testContextInstance; 22 | 23 | /// 24 | ///Gets or sets the test context which provides 25 | ///information about and functionality for the current test run. 26 | /// 27 | public TestContext TestContext 28 | { 29 | get 30 | { 31 | return testContextInstance; 32 | } 33 | set 34 | { 35 | testContextInstance = value; 36 | } 37 | } 38 | 39 | #region Additional test attributes 40 | // 41 | // You can use the following additional attributes as you write your tests: 42 | // 43 | // Use ClassInitialize to run code before running the first test in the class 44 | // [ClassInitialize()] 45 | // public static void MyClassInitialize(TestContext testContext) { } 46 | // 47 | // Use ClassCleanup to run code after all tests in a class have run 48 | // [ClassCleanup()] 49 | // public static void MyClassCleanup() { } 50 | // 51 | // Use TestInitialize to run code before running each test 52 | // [TestInitialize()] 53 | // public void MyTestInitialize() { } 54 | // 55 | // Use TestCleanup to run code after each test has run 56 | // [TestCleanup()] 57 | // public void MyTestCleanup() { } 58 | // 59 | #endregion 60 | 61 | [TestMethod] 62 | public void TestMethod1() 63 | { 64 | // 65 | // TODO: Add test logic here 66 | // 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /UninstallerTests/UninstallerTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {9727E23C-496A-4887-9C87-C5A96F3B05B1} 7 | Library 8 | Properties 9 | UninstallerTests 10 | UninstallerTests 11 | v4.5 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {1e70c6f2-8570-4620-822e-e3f71bf1a0f7} 61 | Uninstall_Wrapper 62 | 63 | 64 | {13c73873-a5ed-42de-97f0-a3b2d7a1d76f} 65 | VS.ConfigurationManager.Support 66 | 67 | 68 | {caccc6e1-fcb8-4dbe-9159-0f8eaa69d27a} 69 | VS.ConfigurationManager 70 | 71 | 72 | 73 | 74 | 75 | 76 | False 77 | 78 | 79 | False 80 | 81 | 82 | False 83 | 84 | 85 | False 86 | 87 | 88 | 89 | 90 | 91 | 92 | 99 | -------------------------------------------------------------------------------- /lib/Branding.rc: -------------------------------------------------------------------------------- 1 | // To use, remove any version information from projects' .rc files and define the following: 2 | // 3 | // #define VER_APP (VER_DLL, none) 4 | // #define VER_LANG_NEUTRAL (optional) 5 | // #define VER_ORIGINAL_FILENAME "foo.exe" 6 | // #define VER_INTERNAL_NAME "foo" 7 | // #define VER_PRODUCT_NAME "Foo" 8 | // #define VER_FILE_DESCRIPTION "Description of foo.exe" 9 | // #include "Branding.rc" 10 | // 11 | // Optionally, define the following before the #include to customize translation block: 12 | // 13 | // #define VER_LANG 0x0000 14 | // #define VER_CP 0x04E4 15 | // #define VER_BLOCK "000004E4" 16 | // 17 | 18 | #include 19 | #include "BrandingInfo.h" 20 | 21 | #ifdef DEBUG 22 | #define VER_DEBUG VS_FF_DEBUG 23 | #define VER_PRIVATE_BUILD VS_FF_PRIVATEBUILD 24 | #define VER_PRE_RELEASE (VS_FF_PRERELEASE | VS_FF_SPECIALBUILD) 25 | #else 26 | #define VER_DEBUG 0 27 | #define VER_PRIVATE_BUILD 0 28 | #define VER_PRE_RELEASE 0 29 | #endif 30 | 31 | #if defined(VER_APP) 32 | #define VER_FILE_TYPE VFT_APP 33 | #elif defined(VER_DLL) 34 | #define VER_FILE_TYPE VFT_DLL 35 | #else 36 | #define VER_FILE_TYPE VFT_UNKNOWN 37 | #endif 38 | 39 | #if defined(VER_LANG_NEUTRAL) 40 | #ifndef VER_LANG 41 | #define VER_LANG 0x0000 42 | #endif 43 | #ifndef VER_CP 44 | #define VER_CP 0x04E4 45 | #endif 46 | #ifndef VER_BLOCK 47 | #define VER_BLOCK "000004E4" 48 | #endif 49 | #else 50 | #ifndef VER_LANG 51 | #define VER_LANG 0x0409 52 | #endif 53 | #ifndef VER_CP 54 | #define VER_CP 0x04E4 55 | #endif 56 | #ifndef VER_BLOCK 57 | #define VER_BLOCK "040904E4" 58 | #endif 59 | #endif 60 | 61 | #define VER_FILE_VERSION MAJOR_VERSION, MINOR_VERSION, BUILD_VERSION, REVISION_NUMBER 62 | #define VER_FILE_VERSION_STRING vwzProductVersion 63 | #define VER_PRODUCT_VERSION MAJOR_VERSION, MINOR_VERSION, BUILD_VERSION, REVISION_NUMBER 64 | #define VER_PRODUCT_VERSION_STRING vwzProductVersion 65 | #define VER_FILE_FLAGS_MASK VS_FFI_FILEFLAGSMASK 66 | #define VER_FILE_FLAGS (VER_DEBUG | VER_PRIVATE_BUILD | VER_PRE_RELEASE) 67 | 68 | #define VER_FILE_OS VOS__WINDOWS32 69 | 70 | #define VER_COMPANY_NAME "Microsoft Corporation" 71 | #ifndef VER_PRODUCT_NAME 72 | #define VER_PRODUCT_NAME "Visual Studio" 73 | #endif 74 | #ifndef VER_FILE_DESCRIPTION 75 | #define VER_FILE_DESCRIPTION "Visual Studio component" 76 | #endif 77 | 78 | #if defined(VER_LEGAL_COPYRIGHT) 79 | #error 80 | #endif 81 | #define VER_LEGAL_COPYRIGHT "Copyright (c) Microsoft Corporation. All rights reserved." 82 | 83 | #if !defined(VER_FILE_SUBTYPE) 84 | #define VER_FILE_SUBTYPE 0 85 | #endif 86 | 87 | #ifdef RC_INVOKED 88 | 89 | VS_VERSION_INFO VERSIONINFO 90 | FILEVERSION VER_FILE_VERSION 91 | PRODUCTVERSION VER_PRODUCT_VERSION 92 | FILEFLAGSMASK VER_FILE_FLAGS_MASK 93 | FILEFLAGS VER_FILE_FLAGS 94 | FILEOS VER_FILE_OS 95 | FILETYPE VER_FILE_TYPE 96 | FILESUBTYPE VER_FILE_SUBTYPE 97 | BEGIN 98 | BLOCK "StringFileInfo" 99 | BEGIN 100 | BLOCK VER_BLOCK 101 | BEGIN 102 | VALUE "CompanyName", VER_COMPANY_NAME 103 | VALUE "FileDescription", VER_FILE_DESCRIPTION 104 | VALUE "FileVersion", VER_FILE_VERSION_STRING 105 | VALUE "InternalName", VER_INTERNAL_NAME 106 | VALUE "LegalCopyright", VER_LEGAL_COPYRIGHT 107 | VALUE "OriginalFilename", VER_ORIGINAL_FILENAME 108 | VALUE "ProductName", VER_PRODUCT_NAME 109 | VALUE "ProductVersion", VER_FILE_VERSION_STRING 110 | END 111 | END 112 | 113 | BLOCK "VarFileInfo" 114 | BEGIN 115 | VALUE "Translation", VER_LANG, VER_CP 116 | END 117 | END 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /lib/Branding.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 1 5 | 0 6 | 7 | alpha 8 | 9 | 10 | 11 | 12 | 13 | $([System.DateTime]::Now.Subtract($([System.DateTime]::Parse("01/01/2000 00:00:00"))).Days) 14 | $([System.Convert]::ToInt32($([MSBuild]::Divide($([System.DateTime]::Now.Subtract($([System.DateTime]::Parse("00:00:00"))).TotalSeconds), 2)))) 15 | $(MajorVersion).$(MinorVersion).$(BuildVersion) 16 | $(MajorVersion).$(MinorVersion).$(BuildVersion)-$(ReleaseToken)-$(RevisionNumber) 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/Common.Cpp.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | true 5 | v120 6 | Unicode 7 | true 8 | 4006,4221 9 | 10 | 11 | false 12 | v120 13 | true 14 | Unicode 15 | false 16 | 4006,4221 17 | 18 | 19 | 20 | Use 21 | Level3 22 | Disabled 23 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 24 | MultiThreadedDebug 25 | true 26 | true 27 | 28 | 29 | true 30 | true 31 | true 32 | 33 | 34 | 35 | 36 | Level3 37 | Use 38 | MaxSpeed 39 | true 40 | true 41 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 42 | MultiThreaded 43 | true 44 | true 45 | 46 | 47 | true 48 | true 49 | true 50 | true 51 | true 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /lib/Common.NuGet.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | <_OutDir>$(OutDir.Trim('\\')) 7 | $(BuildCommand) -BasePath "$(_OutDir)" -Properties "Src=$(SourceDir);Version=$(ProductVersion)" -NoPackageAnalysis -Verbosity Detailed 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/Common.csproj.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Properties 5 | Microsoft.VisualStudio.Setup.$(MSBuildProjectName) 6 | v4.5 7 | 8 | 9 | true 10 | full 11 | false 12 | $(DefineConstants);DEBUG;TRACE 13 | prompt 14 | 4 15 | 16 | 17 | pdbonly 18 | true 19 | $(DefineConstants);TRACE 20 | prompt 21 | 4 22 | 23 | 24 | 25 | true 26 | $(MSBuildThisFileDirectory)Common.ruleset 27 | $(DefineConstants);CODE_ANALYSIS 28 | $(OutDir)\$(AssemblyName).xml 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/Common.csproj.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | $(SolutionDir)obj\$(Configuration)\BrandingInfo.cs 12 | 13 | WriteBrandingInfo; 14 | $(BuildDependsOn); 15 | 16 | 17 | 18 | 19 | Properties\BrandingInfo.cs 20 | 21 | 22 | 27 | 28 | <_BrandingInfoFile Include="$(BrandingInfoFile)"/> 29 | 30 | 31 | <_BrandingInfo Include="System.Reflection.AssemblyCompanyAttribute"> 32 | <_Parameter1>Microsoft Corporation 33 | 34 | <_BrandingInfo Include="System.Reflection.AssemblyCopyrightAttribute"> 35 | <_Parameter1>Copyright (C) Microsoft Corporation. All rights reserved. 36 | 37 | <_BrandingInfo Include="System.Reflection.AssemblyProductAttribute"> 38 | <_Parameter1>Visual Studio 39 | 40 | <_BrandingInfo Include="System.Resources.NeutralResourcesLanguageAttribute"> 41 | <_Parameter1>en 42 | 43 | <_BrandingInfo Include="System.Reflection.AssemblyVersionAttribute"> 44 | <_Parameter1>$(MajorVersion).$(MinorVersion).0.0 45 | 46 | <_BrandingInfo Include="System.Reflection.AssemblyFileVersionAttribute"> 47 | <_Parameter1>$(MajorVersion).$(MinorVersion).$(BuildVersion).$(RevisionNumber) 48 | 49 | 50 | <_BrandingInfo Include="System.Reflection.AssemblyInformationalVersionAttribute"> 51 | <_Parameter1 Condition="'$(ReleaseToken)' == 'rtm'">$(MajorVersion).$(MinorVersion).$(BuildVersion) 52 | <_Parameter1 Condition="'$(ReleaseToken)' != 'rtm'">$(MajorVersion).$(MinorVersion).$(BuildVersion)-$(ReleaseToken)-$(RevisionNumber) 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /lib/Common.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | Microsoft.VisualStudio.Setup 7 | true 8 | 9 | 10 | $([System.IO.Path]::GetFullPath("$(MSBuildThisFileDirectory)..")) 11 | $(SolutionDir)\ 12 | $(SolutionDir)bin\$(Configuration) 13 | $(OutDir)\ 14 | $(OutDir) 15 | $(OutDir.Trim('\\')) 16 | $(SolutionDir)src 17 | 18 | 19 | 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/Common.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/Common.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/Common.vcxproj.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | <_FilesToSign Include="$(TargetPath)" Condition="'$(SignOutput)'!='false'" /> 7 | <_FilesToSign Include="$(AdditionalFilesToSign)" /> 8 | 9 | Microsoft 10 | 11 | 12 | 13 | 14 | 15 | 16 | $(SolutionDir)obj\$(Configuration)\BrandingInfo.h 17 | 18 | WriteBrandingInfo; 19 | $(BuildDependsOn); 20 | 21 | 22 | 23 | 24 | Branding.rc 25 | 26 | 27 | 28 | 29 | $(SolutionDir)lib;$(SolutionDir)obj\$(Configuration);%(AdditionalIncludeDirectories) 30 | 31 | 32 | $(SolutionDir)lib;$(SolutionDir)obj\$(Configuration);%(AdditionalIncludeDirectories) 33 | 34 | 35 | 36 | 41 | 42 | <_BrandingInfoFile Include="$(BrandingInfoFile)"/> 43 | 44 | 45 | <_BrandingInfo Include=" 46 | // auto-generated 47 | #ifndef _BRANDINGINFO_H_ 48 | #define _BRANDINGINFO_H_ 49 | 50 | #define MAJOR_VERSION $(MajorVersion) 51 | #define MINOR_VERSION $(MinorVersion) 52 | #define BUILD_VERSION $(BuildVersion) 53 | #define REVISION_NUMBER $(RevisionNumber) 54 | " /> 55 | <_BrandingInfo Condition="'$(ReleaseToken)' != 'rtm'" Include=" 56 | #define vwzProductVersion "$(MajorVersion).$(MinorVersion).$(BuildVersion)-$(ReleaseToken)-$(RevisionNumber)" 57 | " /> 58 | <_BrandingInfo Condition="'$(ReleaseToken)' == 'rtm'" Include=" 59 | #define vwzProductVersion "$(MajorVersion).$(MinorVersion).$(BuildVersion)" 60 | " /> 61 | <_BrandingInfo Include=" 62 | #endif // _BRANDINGINFO_H_ 63 | " /> 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Visual Studio Uninstaller 2 | Copyright (c) Microsoft Corporation 3 | All rights reserved. 4 | MIT License 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/CommandOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VS.ConfigurationManager.Support; 3 | using System.Globalization; 4 | 5 | namespace Microsoft.VS.Uninstaller 6 | { 7 | /// 8 | /// Option class to describe name-value pairs. 9 | /// 10 | public class CommandOption 11 | { 12 | private static readonly string AppName = "CommandOption"; 13 | /// 14 | /// This is a descriptor of a command that can be run via a console application. 15 | /// 16 | public string Command { get; set; } 17 | /// 18 | /// This is a description showed to the user of what the command does. 19 | /// 20 | public string Description { get; set; } 21 | /// 22 | /// What value should we try to check for comparison? 23 | /// 24 | public string CommandCompareValue { get; set; } 25 | /// 26 | /// What is the value of the parameter 27 | /// 28 | public string Value { get; set; } 29 | /// 30 | /// Creating a new option takes the 4 properties values as parameters 31 | /// 32 | /// 33 | /// 34 | /// 35 | /// 36 | public CommandOption(string command, string description, string commandcomparevalue, string value) 37 | { 38 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Creating option: Command: {0}, Description: {1}, Keyword: {2}, Comparison value: {3}", command, description, commandcomparevalue, value), Logger.MessageLevel.Verbose, AppName); 39 | Command = command; 40 | Description = description; 41 | CommandCompareValue = commandcomparevalue; 42 | Value = value; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/ConsoleOperations.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager; 2 | using Microsoft.VS.ConfigurationManager.Support; 3 | using System; 4 | using System.Globalization; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.IO; 9 | 10 | namespace Microsoft.VS.Uninstaller 11 | { 12 | static internal class ConsoleOperations 13 | { 14 | private const string _explorer = "Explorer.exe"; 15 | private const int _width = 85; 16 | private const int _height = 45; 17 | 18 | public static List Options = new List(); 19 | 20 | private static string line = new StringBuilder().Append('-', 85).ToString(); 21 | private static string path = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "DataFiles"); 22 | public const string AppName = "ConsoleOperations"; 23 | public const string COMMAND_COMPARE_DIRECTORY = "DIR"; 24 | public const string COMMAND_COMPARE_LIST = "LIST"; 25 | public const string COMMAND_COMPARE_CREATE = "CREATE"; 26 | public const string COMMAND_COMPARE_LOAD = "LOAD"; 27 | public const string COMMAND_COMPARE_SELECT = "SELECT"; 28 | public const string COMMAND_COMPARE_INSTALLED = "INSTALLED"; 29 | public const string COMMAND_COMPARE_VSINSTALLED = "VSINSTALLED"; 30 | public const string COMMAND_COMPARE_UNINSTALL = "UNINSTALL"; 31 | public const string COMMAND_COMPARE_MSIS = "MSIS"; 32 | public const string COMMAND_COMPARE_TEMP = "TEMP"; 33 | public const string COMMAND_COMPARE_LOGSELECTED = "LOG"; 34 | 35 | internal static Primitives PrimitiveObject { get; set; } 36 | 37 | static internal string MsgRelease { get; set; } 38 | 39 | static internal void SecurityWarning() 40 | { 41 | Console.WriteLine(Logger.Log("Not running as elevated or administrator.", Logger.MessageLevel.Information, AppName)); 42 | Console.ForegroundColor = ConsoleColor.Red; 43 | Console.WriteLine(Logger.Log("Possible error condition found.", Logger.MessageLevel.Information, AppName)); 44 | Console.ResetColor(); 45 | Console.WriteLine(Logger.Log("If you are not running with elevated permissions, the uninstall processes can result in errors. For optimal results, please run command console as administrator.", Logger.MessageLevel.Information, AppName)); 46 | Console.ForegroundColor = ConsoleColor.White; 47 | Console.WriteLine("\r\nPress enter to continue."); 48 | Console.ResetColor(); 49 | Console.ReadLine(); 50 | } 51 | 52 | static internal void SetConsoleAttributes() 53 | { 54 | Console.WindowHeight = Console.LargestWindowHeight < _height ? Console.LargestWindowHeight : _height; 55 | Console.WindowWidth = _width; 56 | Console.Title = "WixPdb sourced uninstall driver"; 57 | } 58 | 59 | static internal void OpenTempDirectory() 60 | { 61 | Console.WriteLine(Logger.Log("Opening temp directory", Logger.MessageLevel.Verbose, AppName)); 62 | Utility.ExecuteProcess(_explorer, Utility.TempDir); 63 | } 64 | 65 | static internal void SetUpLogging() 66 | { 67 | var time = DateTime.Now.ToString("MM-dd-yy-hhmmss", CultureInfo.InvariantCulture); 68 | var logfilepath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "dd_Microsoft.VS.Uninstaller_" + System.IO.Path.ChangeExtension(time, "log")); 69 | Logger.LogLocation = logfilepath; 70 | Logger.LoggingLevel = Logger.MessageLevel.Verbose; 71 | Logger.ConsoleOutput = true; 72 | } 73 | 74 | static internal void GetUsage() 75 | { 76 | 77 | var sb = new StringBuilder(); 78 | 79 | sb.AppendLine("Please use the key words to execute a command.\r\n"); 80 | sb.AppendLine("Command".PadRight(30) + "Key words".PadRight(15) + "Description".PadRight(40)); 81 | sb.AppendLine(line); 82 | 83 | Console.ForegroundColor = ConsoleColor.White; 84 | Console.WriteLine(sb.ToString()); 85 | Console.ResetColor(); 86 | 87 | foreach (CommandOption op in GetOptions()) 88 | { 89 | sb = new StringBuilder(); 90 | sb.Append(op.Command.PadRight(30)); 91 | sb.Append(op.CommandCompareValue.PadRight(15)); 92 | Console.ForegroundColor = ConsoleColor.White; 93 | Console.Write(sb.ToString()); 94 | Console.ResetColor(); 95 | FormatOutput(op.Description, 30, 45); 96 | } 97 | Console.ForegroundColor = ConsoleColor.White; 98 | sb = new StringBuilder(); 99 | 100 | sb.AppendLine(); 101 | sb.AppendLine(String.Format(CultureInfo.InvariantCulture, "Working directory: {0}", PrimitiveObject.DataFilesPath)); 102 | sb.AppendLine(String.Format(CultureInfo.InvariantCulture, "Debug: {0}", PrimitiveObject.DebugReporting.ToString())); 103 | sb.AppendLine(String.Format(CultureInfo.InvariantCulture, "Do Not Process: {0}", PrimitiveObject.DoNotExecuteProcess.ToString())); 104 | sb.AppendLine(); 105 | sb.AppendLine("Enter in a key word to run the next command or 'quit' to exit"); 106 | sb.AppendLine(); 107 | sb.Append("> "); 108 | Console.Write(sb.ToString()); 109 | sb = null; 110 | } 111 | 112 | private static ICollection GetOptions() 113 | { 114 | if (Options.FirstOrDefault() == null) 115 | { 116 | Options.Add(new CommandOption("Directory", "Identifies which directory files are loaded from and saved to. This has no impact on the Content directory where the WixPdbs go.", COMMAND_COMPARE_DIRECTORY, null)); 117 | Options.Add(new CommandOption("List", "Lists all the bundles that the application is aware of.", COMMAND_COMPARE_LIST, null)); 118 | Options.Add(new CommandOption("Create config files", "Creates configuration files from wixpdbs.", COMMAND_COMPARE_CREATE, null)); 119 | Options.Add(new CommandOption("Load config files", "Loads configuration files from disk", COMMAND_COMPARE_LOAD, null)); 120 | Options.Add(new CommandOption("Select", "Allows you to choose which bundle(s) to uninstall", COMMAND_COMPARE_SELECT, null)); 121 | Options.Add(new CommandOption("Show what is installed", "Shows what is currently installed on this machine.", COMMAND_COMPARE_INSTALLED, null)); 122 | Options.Add(new CommandOption("Show what VS installs what", "Shows of the things that are installed, which ones are installed by Visual Studio", COMMAND_COMPARE_VSINSTALLED, null)); 123 | Options.Add(new CommandOption("Uninstall", "Triggers an uninstall of the selected bundle(s)", COMMAND_COMPARE_UNINSTALL, null)); 124 | Options.Add(new CommandOption("Uninstall MSIs", "Used after uninstalling the selected bundle(s) to remove any loose MSIs left behind.", COMMAND_COMPARE_MSIS, null)); 125 | Options.Add(new CommandOption("Open temp dir", "Opens the temporary directory where logs are stored.", COMMAND_COMPARE_TEMP, null)); 126 | } 127 | return Options; 128 | } 129 | 130 | private static void FormatOutput(string textselection, int pos, int startpad) 131 | { 132 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Formatting output: {0}", textselection), Logger.MessageLevel.Verbose, AppName); 133 | if (textselection.Length > pos && textselection.IndexOf(' ', pos) != -1) 134 | { 135 | var space = textselection.IndexOf(' ', pos); 136 | 137 | Console.WriteLine(textselection.Substring(0, space).PadLeft(pos)); 138 | 139 | while (textselection.Length >= pos && textselection.IndexOf(' ', pos) != -1) 140 | { 141 | textselection = textselection.Length >= pos ? textselection.Substring(space, textselection.Length - space).Trim() : textselection; 142 | space = textselection.IndexOf(' ', pos >= textselection.Length ? textselection.Length : pos); 143 | Console.WriteLine(" ".PadLeft(startpad) + textselection.Substring(0, space == -1 ? textselection.Length : space)); 144 | } 145 | } 146 | else 147 | { 148 | Console.WriteLine(textselection); 149 | } 150 | Logger.Log("Formatting output ended", Logger.MessageLevel.Verbose, AppName); 151 | } 152 | 153 | 154 | static internal void ChangeWorkingDirectory(string[] cmdset) 155 | { 156 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Change working directory command started."), Logger.MessageLevel.Verbose, AppName); 157 | var dir = cmdset[1].Replace(COMMAND_COMPARE_DIRECTORY, ""); 158 | 159 | PrimitiveObject.DataFilesPath = (String.IsNullOrEmpty(dir)) ? PrimitiveObject.DataFilesPath : dir; 160 | var di = new DirectoryInfo(dir); 161 | 162 | if (!di.Exists) { di.Create(); } 163 | 164 | PrimitiveObject.DataFilesPath = dir; 165 | 166 | Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Working directory has been reset to {0}", PrimitiveObject.DataFilesPath)); 167 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Change working directory: {0}", PrimitiveObject.DataFilesPath), Logger.MessageLevel.Information, AppName); 168 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Change working directory command ended."), Logger.MessageLevel.Verbose, AppName); 169 | } 170 | 171 | static internal void SetupPrimitivesValues(bool debug, bool donotprocess) 172 | { 173 | // uti.bDebug = op.Debug; ip.DebugReporting = op.Debug; 174 | PrimitiveObject.MachineArchitectureConfiguration = SystemSettings.Is64() ? ArchitectureConfiguration.x64 : ArchitectureConfiguration.x86; 175 | PrimitiveObject.MachineOSVersion = SystemSettings.Version(); 176 | 177 | PrimitiveObject.Filters.Clear(); 178 | PrimitiveObject.UninstallActions.Clear(); 179 | 180 | VisualStudioSpecific.VSFilters(PrimitiveObject); 181 | VisualStudioSpecific.VSUninstallActions(PrimitiveObject); 182 | 183 | PrimitiveObject.DataFilesPath = path; 184 | PrimitiveObject.DoNotExecuteProcess = donotprocess; 185 | PrimitiveObject.DebugReporting = debug; 186 | 187 | //Initialize 188 | PrimitiveObject.Initialize(); 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/DataFile.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/src/Uninstall_Wrapper/DataFile.bin -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager; 2 | using Microsoft.VS.ConfigurationManager.Support; 3 | using Microsoft.Win32; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Globalization; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Text; 12 | 13 | namespace Microsoft.VS.Uninstaller 14 | { 15 | internal class Program 16 | { 17 | private const string _explorer = "Explorer.exe"; 18 | private const string AppName = "Console Application"; 19 | 20 | private static bool _debug; 21 | private static bool _donotprocess; 22 | 23 | #region Private Methods 24 | 25 | private static int Main(string[] args) 26 | { 27 | string wixpdbsPathsFile = string.Empty; 28 | string[] wixpdbsPaths = null; 29 | string dataFilePath = string.Empty; 30 | //args = new string[] { "noprocess", @"/wixpdbs:C:\Users\user\Desktop\test\paths.txt" }; 31 | //args = new string[] { "noprocess", @"/binfile:C:\Users\user\Desktop\test\DataFile.bin" }; 32 | //args = new string[] { "noprocess", @"/binfile:C:\Users\user\Desktop\test\DataFile.bin", @"/wixpdbs:\\myshare\Drops\user\wixpdbsPS\sub\Files.txt" }; 33 | if (args != null && args.Count() > 0) 34 | { 35 | foreach (var arg in args) 36 | { 37 | switch(arg.ToLowerInvariant()) 38 | { 39 | case "help": 40 | case "/help": 41 | case "/?": 42 | PrintUsage(); 43 | return 0; 44 | case "break": 45 | Console.WriteLine("Program stopped, please attach debugger and then hit any key to continue."); 46 | Console.ReadKey(true); 47 | break; 48 | case "debug": 49 | _debug = true; 50 | break; 51 | case "noprocess": 52 | _donotprocess = true; 53 | break; 54 | default: 55 | // Path to the file containing a list of paths to the wixpdbs. 56 | // e.g. /wixpdbs:c:\myPaths.txt 57 | if (arg.StartsWith("/wixpdbs:", StringComparison.OrdinalIgnoreCase)) 58 | { 59 | wixpdbsPathsFile = arg.Substring("/wixpdbs:".Length); 60 | wixpdbsPaths = File.ReadAllLines(wixpdbsPathsFile); 61 | } 62 | // Path to the file containing the DataFile.bin; if no file is passed in, it will use the embedded one. 63 | // e.g. /binfile:C:\DataFile.bin 64 | else if (arg.StartsWith("/binfile:", StringComparison.OrdinalIgnoreCase)) 65 | { 66 | dataFilePath = arg.Substring("/binfile:".Length); 67 | } 68 | break; 69 | } 70 | } 71 | } 72 | 73 | var ip = new Primitives(); 74 | 75 | ConsoleOperations.PrimitiveObject = ip; 76 | ConsoleOperations.SetUpLogging(); 77 | 78 | ip.DoNotExecuteProcess = _donotprocess; 79 | ip.DebugReporting = _debug; 80 | 81 | try 82 | { 83 | // Check for permissions to run uninstall actions 84 | var elev = new ElevationDetection(); 85 | if (!elev.Level) 86 | { 87 | ConsoleOperations.SecurityWarning(); 88 | return 0; 89 | } 90 | else 91 | { 92 | Logger.Log("Running elevated or as administrator", Logger.MessageLevel.Information, AppName); 93 | } 94 | elev = null; 95 | 96 | // Define base variables for use of primitives object; adding filters, uninstall actions, logging location, and default location of data files 97 | ConsoleOperations.SetupPrimitivesValues(_debug, _donotprocess); 98 | 99 | // If /wixpdbs is used, .bin data file is generated for the user. 100 | if (wixpdbsPaths != null && wixpdbsPaths.Length > 0) 101 | { 102 | if (!string.IsNullOrEmpty(dataFilePath) && File.Exists(dataFilePath)) 103 | { 104 | Logger.LogWithOutput(string.Format("Loading from {0}", dataFilePath)); 105 | ip.LoadFromDataFile(dataFilePath); 106 | } 107 | 108 | Logger.LogWithOutput("Generating data file from wixpdbs ...."); 109 | foreach (var wixpdbPath in wixpdbsPaths) 110 | { 111 | ip.LoadFromWixpdb(wixpdbPath); 112 | } 113 | 114 | ip.SaveToDataFile(); 115 | Logger.Log("Data File generation operation is successful. Exiting ...", Logger.MessageLevel.Information, AppName); 116 | return 0; 117 | } 118 | // Else uninstall Visual Studio 2013/2015/vNext 119 | else 120 | { 121 | if (!string.IsNullOrEmpty(dataFilePath) && File.Exists(dataFilePath)) 122 | { 123 | ip.LoadFromDataFile(dataFilePath); 124 | } 125 | else 126 | { 127 | // load from embedded. 128 | var assembly = Assembly.GetExecutingAssembly(); 129 | var dataFile = "Microsoft.VisualStudio.Setup.DataFile.bin"; 130 | 131 | using (Stream stream = assembly.GetManifestResourceStream(dataFile)) 132 | { 133 | ip.LoadFromDataFile(stream); 134 | } 135 | } 136 | 137 | ip.InstalledVisualStudioReport(); 138 | Logger.LogWithOutput("WARNING: This executable is designed to cleanup/scorch all Preview/RC/RTM releases of Visual Studio 2013, Visual Studio 2015 and Visual Studio vNext."); 139 | Logger.LogWithOutput("It should be used as the last resort to clean up the user's system before resorting to reimaging the machine. "); 140 | Logger.LogWithOutput("Would you like to continue? [Y/N]"); 141 | var action = Console.ReadLine(); 142 | if (!string.IsNullOrEmpty(action) && action.StartsWith("y", StringComparison.OrdinalIgnoreCase)) 143 | { 144 | // cache the vs dirs in memory before uninstalling. 145 | var vsDirs = GetVisualStudioInstallationDirs(); 146 | 147 | int exitCode = ip.Uninstall(); 148 | 149 | if (exitCode == 3010) 150 | { 151 | Logger.LogWithOutput("Bundle requested to reboot the system. Please reboot your computer and run this application again."); 152 | return 3010; 153 | } 154 | ip.CleanupVisualStudioFolders(vsDirs); 155 | ip.CleanupSecondaryInstallerCache(); 156 | ip.CleanupVisualStudioRegistryHives(); 157 | } 158 | else 159 | { 160 | Logger.LogWithOutput("Exiting ..."); 161 | } 162 | } 163 | } 164 | catch (Exception ex) 165 | { 166 | Logger.Log(ex, AppName); 167 | } 168 | finally 169 | { 170 | ip.Dispose(); 171 | } 172 | 173 | return 0; 174 | } 175 | 176 | private static IEnumerable GetVisualStudioInstallationDirs() 177 | { 178 | List vsDirs = new List(); 179 | 180 | var vsVers = new string[] { "12.0", "14.0", "15.0" }; 181 | 182 | // %AppData%\Microsoft\VisualStudio\14.0 & 12.0 & 15.0 183 | // %LocalAppData%\Microsoft\VisualStudio\14.0 & 12.0 & 15.0 184 | // %LocalAppData%\Microsoft\VSCommon\14.0 & 12.0 & 15.0 185 | var appDataRoot = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); 186 | var localAppDataRoot = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 187 | 188 | foreach (var vsVer in vsVers) 189 | { 190 | if (Environment.Is64BitOperatingSystem) 191 | { 192 | var installDir = (string)Registry.GetValue( 193 | string.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\{0}\\", vsVer), 194 | "ShellFolder", 195 | null); 196 | if (!string.IsNullOrEmpty(installDir)) 197 | { 198 | vsDirs.Add(installDir); 199 | } 200 | } 201 | else 202 | { 203 | var installDir = (string)Registry.GetValue( 204 | string.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\{0}\\", vsVer), 205 | "ShellFolder", 206 | null); 207 | if (!string.IsNullOrEmpty(installDir)) 208 | { 209 | vsDirs.Add(installDir); 210 | } 211 | } 212 | 213 | vsDirs.Add(Path.Combine(appDataRoot, "Microsoft", "VisualStudio", vsVer)); 214 | vsDirs.Add(Path.Combine(localAppDataRoot, "Microsoft", "VisualStudio", vsVer)); 215 | vsDirs.Add(Path.Combine(localAppDataRoot, "Microsoft", "VSCommon", vsVer)); 216 | } 217 | 218 | return vsDirs; 219 | } 220 | 221 | private static void PrintUsage() 222 | { 223 | Console.WriteLine("Welcome to Total Uninstaller."); 224 | Console.WriteLine("Running this application will remove Visual Studio 2013/2015/vNext completely."); 225 | Console.WriteLine("It should be used as the last resort to clean up your machine."); 226 | Console.WriteLine("----------- Normal Usage --------------"); 227 | Console.WriteLine("Please run this application as administrator without any parameter."); 228 | } 229 | 230 | #endregion Private Methods 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/TotalUninstaller EULA.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/src/Uninstall_Wrapper/TotalUninstaller EULA.docx -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/Uninstall_Wrapper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | app.manifest 7 | {1E70C6F2-8570-4620-822E-E3F71BF1A0F7} 8 | publish\ 9 | true 10 | Disk 11 | false 12 | Foreground 13 | 7 14 | Days 15 | false 16 | false 17 | true 18 | 0 19 | 1.0.0.%2a 20 | false 21 | false 22 | true 23 | v4.0 24 | 25 | 26 | 27 | 28 | 29 | Microsoft.VS.Uninstaller.Program 30 | 31 | 32 | TRACE;DEBUG;DOTNETFRAMEWORK35 33 | ManagedMinimumRules.ruleset 34 | false 35 | false 36 | 37 | 38 | Setup.ForcedUninstall 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Always 60 | 61 | 62 | 63 | Always 64 | 65 | 66 | 67 | 68 | False 69 | .NET Framework 3.5 SP1 70 | true 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Microsoft 79 | 80 | 81 | 82 | 83 | {13c73873-a5ed-42de-97f0-a3b2d7a1d76f} 84 | VS.ConfigurationManager.Support 85 | 86 | 87 | {caccc6e1-fcb8-4dbe-9159-0f8eaa69d27a} 88 | VS.ConfigurationManager 89 | 90 | 91 | 92 | 93 | 94 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/VisualStudioSpecifc.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager; 2 | using Microsoft.VS.ConfigurationManager.Support; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Microsoft.VS.Uninstaller 7 | { 8 | internal static class VisualStudioSpecific 9 | { 10 | internal static void VSFilters(Primitives ip) 11 | { 12 | ip.Filters.Add(Filter.CreateFilter("Replace Visual Studio with shorter version", "Microsoft Visual Studio ", "VS ")); 13 | ip.Filters.Add(Filter.CreateFilter("Shorten Microsoft", "Microsoft ", "MS ")); 14 | ip.Filters.Add(Filter.CreateFilter("Shorten Team Foundation Server", "Team Foundation Server ", "TFS ")); 15 | ip.Filters.Add(Filter.CreateFilter("Shorten Visual C++", "Visual C++ ", "VC ")); 16 | } 17 | 18 | internal static void VSUninstallActions(Primitives ip) 19 | { 20 | 21 | ip.UninstallActions.Add( 22 | UninstallAction.CreateUninstallAction( 23 | new List { ArchitectureConfiguration.x86, ArchitectureConfiguration.x64 }, 24 | new List { OperatingSystemConfiguration.Windows81 }, 25 | "2999226", 26 | UninstallAction.TemplateType.Pre, 27 | UninstallAction.WixObjectType.MSU 28 | ) 29 | ); 30 | ip.UninstallActions.Add( 31 | UninstallAction.CreateUninstallAction( 32 | new List { ArchitectureConfiguration.x86, ArchitectureConfiguration.x64 }, 33 | new List { OperatingSystemConfiguration.Windows8 }, 34 | "2999226", 35 | UninstallAction.TemplateType.Pre, 36 | UninstallAction.WixObjectType.MSU 37 | ) 38 | ); 39 | 40 | ip.UninstallActions.Add( 41 | UninstallAction.CreateUninstallAction( 42 | new List { ArchitectureConfiguration.x86, ArchitectureConfiguration.x64 }, 43 | new List { OperatingSystemConfiguration.Windows7 }, 44 | "2999226", 45 | UninstallAction.TemplateType.Pre, 46 | UninstallAction.WixObjectType.MSU 47 | ) 48 | ); 49 | 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 58 | 59 | 60 | 73 | 74 | -------------------------------------------------------------------------------- /src/Uninstall_Wrapper/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/ArchitectureConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Microsoft.VS.ConfigurationManager.Support 7 | { 8 | /// 9 | /// Defining an architecture as x86 or 64-bit 10 | /// 11 | public class ArchitectureConfiguration 12 | { 13 | /// 14 | /// Setting the property value 15 | /// 16 | /// 17 | public ArchitectureConfiguration(string value) { Value = value; } 18 | /// 19 | /// Property for holding value 20 | /// 21 | public string Value { get; set; } 22 | 23 | private static readonly List _archlist = new List(); 24 | /// 25 | /// 64-bit property for use in the UninstallAction class 26 | /// 27 | public static ArchitectureConfiguration x64 { get { return new ArchitectureConfiguration("x64"); } } 28 | /// 29 | /// 32-bit property for use in the UninstallAction class 30 | /// 31 | public static ArchitectureConfiguration x86 { get { return new ArchitectureConfiguration("x86"); } } 32 | /// 33 | /// Generating a list of Architectures supported. 34 | /// 35 | public static ICollection Architectures() 36 | { 37 | Logger.Log("Creating list of architectures", Logger.MessageLevel.Information, "ArchitectureConfiguration"); 38 | if (_archlist == null) 39 | { 40 | _archlist.Add(x64); 41 | _archlist.Add(x86); 42 | } 43 | return _archlist; 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/ConfigurationManagerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Microsoft.VS.ConfigurationManager.Support 5 | { 6 | /// 7 | /// Custom exceptions derived from base class 8 | /// 9 | [Serializable()] 10 | public class ConfigurationManagerException : Exception, ISerializable 11 | { 12 | /// 13 | /// Default constructor for exception 14 | /// 15 | public ConfigurationManagerException() { } 16 | /// 17 | /// Constructor with message only 18 | /// 19 | /// 20 | public ConfigurationManagerException(string message) : base(message) { } 21 | /// 22 | /// Constructor with message and inner exception 23 | /// 24 | /// 25 | /// 26 | public ConfigurationManagerException(string message, Exception inner) : base(message, inner) { } 27 | /// 28 | /// Protected constructor for exception 29 | /// 30 | /// 31 | /// 32 | protected ConfigurationManagerException(SerializationInfo info, StreamingContext context) : base(info, context) { } 33 | } 34 | 35 | /// 36 | /// Exception to let user know that a reboot is required. 37 | /// 38 | [Serializable()] 39 | public class RebootRequiredException : ConfigurationManagerException 40 | { 41 | /// 42 | /// Default constructor for exception 43 | /// 44 | public RebootRequiredException() { } 45 | /// 46 | /// Constructor with message only 47 | /// 48 | /// 49 | public RebootRequiredException(string message) : base(message) { } 50 | /// 51 | /// Constructor with message and inner exception 52 | /// 53 | /// 54 | /// 55 | public RebootRequiredException(string message, Exception inner) : base(message, inner) { } 56 | /// 57 | /// Protected constructor for exception 58 | /// 59 | /// 60 | /// 61 | protected RebootRequiredException(SerializationInfo info, StreamingContext context) : base(info, context) { } 62 | } 63 | 64 | /// 65 | /// Exception when WixPdbs are unavailable and configuration files are unavailable. 66 | /// 67 | [Serializable()] 68 | public class NoSourceFilesAvailableForParsingException : ConfigurationManagerException 69 | { 70 | /// 71 | /// Default constructor for exception 72 | /// 73 | public NoSourceFilesAvailableForParsingException() { } 74 | /// 75 | /// Constructor with message only 76 | /// 77 | /// 78 | public NoSourceFilesAvailableForParsingException(string message) : base(message) { } 79 | /// 80 | /// Constructor with message and inner exception 81 | /// 82 | /// 83 | /// 84 | public NoSourceFilesAvailableForParsingException(string message, Exception inner) : base(message, inner) { } 85 | /// 86 | /// Protected constructor for exception 87 | /// 88 | /// 89 | /// 90 | protected NoSourceFilesAvailableForParsingException(SerializationInfo info, StreamingContext context) : base(info, context) { } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/ElevationDetection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Principal; 3 | using System.Threading; 4 | 5 | namespace Microsoft.VS.ConfigurationManager.Support 6 | { 7 | /// 8 | /// Class to determine if a user has run the application with administrative priviledges. 9 | /// 10 | public class ElevationDetection 11 | { 12 | private const string AppName = "ElevationDetection"; 13 | #region Constructor: 14 | /// 15 | /// Constructor that sets whether permission is available to read the registry and uninstall. 16 | /// 17 | public ElevationDetection() 18 | { 19 | // Invoke Method On Creation: 20 | Elevate(); 21 | } 22 | 23 | #endregion Constructor: 24 | /// 25 | /// Property that defines a user has permission. 26 | /// 27 | public bool Level { get; set; } 28 | 29 | private void Elevate() 30 | { 31 | try 32 | { 33 | Logger.Log("Identifying if elevation has been provided", Logger.MessageLevel.Information, AppName); 34 | // Was this thread started with admin priviledges? 35 | var domain = Thread.GetDomain(); 36 | 37 | domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); 38 | var role = (WindowsPrincipal)Thread.CurrentPrincipal; 39 | 40 | if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major < 6) 41 | { 42 | Logger.Log("Lower OS found (< 6.0 revision or not Win32 NT)", Logger.MessageLevel.Information, AppName); 43 | Level = false; 44 | // Todo: Exception/ Exception Log 45 | } 46 | else 47 | { 48 | if (!role.IsInRole(WindowsBuiltInRole.Administrator)) 49 | { 50 | Logger.Log("Not part of the Administrator role", Logger.MessageLevel.Information, AppName); 51 | Level = false; 52 | // Todo: "Exception Log / Exception" 53 | } 54 | else 55 | { 56 | Logger.Log("Part of the Administrator role", Logger.MessageLevel.Information, AppName); 57 | Level = true; 58 | } 59 | } // Initial Else 'Close' 60 | } 61 | catch (Exception ex) 62 | { 63 | Logger.Log(ex, AppName); 64 | Level = false; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace Microsoft.VS.ConfigurationManager.Support 8 | { 9 | /// All logging capabilites for class run through these trace listeners 10 | public static class Logger 11 | { 12 | private const string displayName = "VS.ConfigurationManager"; 13 | private const string _warning = "Warning"; 14 | private const string _error = "ERROR"; 15 | private const string _info = "Info"; 16 | private static readonly object _syncObject = new object(); 17 | private static string _logLocation = String.Empty; 18 | private static string _ThisAssembly = String.Empty; 19 | 20 | /// Defining event types that can be written out. 21 | public enum MessageLevel 22 | { 23 | /// No value provided and default to writing errors out 24 | None = 0, 25 | 26 | /// Write an error message 27 | Error = 1, 28 | 29 | /// Write a warning message 30 | Warning = 2, 31 | 32 | /// Write informational message 33 | Information = 3, 34 | 35 | /// Write a verbose message 36 | Verbose = 4 37 | } 38 | 39 | /// 40 | /// Setting debug property to do verbose logging or generating warnings only. 41 | /// 42 | public static bool Debug { get; set; } 43 | 44 | /// 45 | /// Define what level of logging should be done 46 | /// 47 | public static MessageLevel LoggingLevel { get; set; } 48 | 49 | /// Log location used for this instance of the object 50 | public static string LogLocation 51 | { 52 | get 53 | { 54 | return _logLocation; 55 | } 56 | set 57 | { 58 | // If LogLocation is not set and a location has been passed in, then use the temp directory. 59 | _logLocation = String.IsNullOrEmpty(value) ? System.IO.Path.GetTempPath() : value; 60 | 61 | // Add a unique filename if a file name is not already in place 62 | _logLocation = _logLocation.ToUpperInvariant().Contains(".LOG") ? _logLocation : 63 | System.IO.Path.ChangeExtension 64 | ( 65 | System.IO.Path.Combine 66 | ( 67 | _logLocation, "ApplicationLog-" + DateTime.Now.ToString("MM-dd-yy-hhmmss", CultureInfo.InvariantCulture) 68 | ), "log" 69 | ); 70 | 71 | } 72 | } 73 | 74 | /// 75 | /// Property to force information to the console window 76 | /// 77 | public static bool ConsoleOutput { get; set; } 78 | #region Log Overloads 79 | 80 | /// 81 | /// With passed exceptions, information is written to log 82 | /// 83 | /// 84 | /// 85 | /// 86 | public static string Log(Exception ex, string sourcelocation) 87 | { 88 | return Log(String.Format(CultureInfo.InvariantCulture, "Caller: {0} Msg: {1}", GetCurrentMethod(new StackTrace(ex, true)), ex.Message), MessageLevel.Error, sourcelocation); 89 | } 90 | 91 | /// With passed exceptions, information is written to log 92 | /// 93 | public static string Log(Exception ex) 94 | { 95 | return Log(ex.Message, MessageLevel.Error, GetCurrentMethod(new StackTrace(ex, true))); 96 | } 97 | 98 | /// 99 | /// Logging source location as well as event level 100 | /// 101 | /// 102 | /// 103 | /// 104 | /// 105 | public static string Log(string logtext, MessageLevel eventlevel = MessageLevel.Information, string source = "Default") 106 | { 107 | // Ensure that stream writer is instantiated 108 | var consoleoutput = String.Empty; 109 | source = String.IsNullOrEmpty(source) ? "Default" : source; 110 | 111 | if (Debug || ConsoleOutput || eventlevel == MessageLevel.Error || eventlevel == MessageLevel.Warning) 112 | { 113 | consoleoutput = logtext; 114 | } 115 | try 116 | { 117 | switch (eventlevel) 118 | { 119 | case 0: 120 | break; 121 | case MessageLevel.Error: 122 | GenerateOutputMessage(logtext, MessageLevel.Error, source); 123 | break; 124 | case MessageLevel.Warning: 125 | GenerateOutputMessage(logtext, MessageLevel.Warning, source); 126 | break; 127 | case MessageLevel.Information: 128 | GenerateOutputMessage(logtext, MessageLevel.Information, source); 129 | break; 130 | case MessageLevel.Verbose: 131 | GenerateOutputMessage(logtext, MessageLevel.Verbose, source); 132 | break; 133 | } 134 | } 135 | catch(IOException ex) { 136 | System.Diagnostics.Debug.Write(String.Format(CultureInfo.InvariantCulture, "IO Exception hit: {0}", ex.Message)); 137 | } 138 | 139 | return consoleoutput; 140 | 141 | } 142 | 143 | /// 144 | /// Logging source location as well as event level 145 | /// 146 | /// 147 | /// 148 | /// 149 | /// 150 | /// 151 | public static string LogWithOutput(string logtext, MessageLevel eventlevel = MessageLevel.Information, string source = "Default") 152 | { 153 | Console.WriteLine(logtext); 154 | return Log(logtext, eventlevel, source); 155 | } 156 | 157 | #endregion Log Overloads 158 | 159 | private static void GenerateOutputMessage(string logtext, MessageLevel prefix, string source) 160 | { 161 | switch (LoggingLevel >= prefix) 162 | { 163 | case true: 164 | var _sb = new StringBuilder(); 165 | var logtimestamp = DateTime.Now.ToString("MM-dd-yyyy hh:mm:ss:sss.fff", CultureInfo.InvariantCulture); 166 | _sb.Append(logtimestamp.PadRight(30)); 167 | _sb.Append(prefix.ToString().PadRight(30)); 168 | _sb.Append(source.PadRight(40)); 169 | _sb.Append(GetCurrentMethod(GetCaller()).PadRight(30)); 170 | _sb.Append(logtext); 171 | lock (_syncObject) 172 | { 173 | using (StreamWriter sw = new StreamWriter(LogLocation, true)) { sw.WriteLine(_sb.ToString()); } 174 | } 175 | break; 176 | case false: 177 | break; 178 | } 179 | } 180 | 181 | private static StackFrame GetCaller() 182 | { 183 | // TODO: GetCaller frame is returning method names that do not appear to be correct in the log 184 | 185 | // Get our own, current namespace name. 186 | _ThisAssembly = String.IsNullOrEmpty(_ThisAssembly) ? new StackFrame(0).GetMethod().DeclaringType.ToString() : _ThisAssembly; 187 | 188 | // We’ll use this to walk the stack looking for a 189 | // method name that is not the same as our method 190 | // name—that is, the name of the method that called 191 | // this method name. 192 | var trace = new StackTrace(true); 193 | 194 | // Look for the first occurence of a stack frame that 195 | // contains a namespace name that is different from our 196 | // own. 197 | var i = 0; 198 | var frame = trace.GetFrame(i); 199 | while ((frame.GetMethod().DeclaringType.FullName.ToUpperInvariant() == _ThisAssembly.ToUpperInvariant()) && (i < trace.FrameCount)) 200 | { 201 | i++; 202 | frame = trace.GetFrame(i); 203 | } 204 | 205 | return frame; 206 | } 207 | 208 | private static string GetCurrentMethod(StackFrame sf) 209 | { 210 | return sf.GetMethod().Name; 211 | } 212 | 213 | private static string GetCurrentMethod(StackTrace st = null) 214 | { 215 | return GetCurrentMethod(st.GetFrame(0)); 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Microsoft.VS.ConfigurationManager.Support 5 | { 6 | internal static class NativeMethods 7 | { 8 | #region Read 64bit Reg from 32bit app 9 | [DllImport("Advapi32.dll")] 10 | internal static extern uint RegOpenKeyEx( 11 | UIntPtr hKey, 12 | string lpSubKey, 13 | uint ulOptions, 14 | int samDesired, 15 | out int phkResult); 16 | 17 | [DllImport("Advapi32.dll")] 18 | internal static extern uint RegCloseKey(int hKey); 19 | 20 | /// 21 | /// Importing call to allow reading under 3.5 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | [DllImport("advapi32.dll", EntryPoint = @"RegQueryValueEx")] 31 | internal static extern int RegQueryValueEx( 32 | int hKey, string lpValueName, 33 | int lpReserved, 34 | ref uint lpType, 35 | System.Text.StringBuilder lpData, 36 | ref uint lpcbData); 37 | #endregion 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/OperatingSystemConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Microsoft.VS.ConfigurationManager.Support 7 | { 8 | /// 9 | /// Listing of operating systems and their versions for use with UninstallAction 10 | /// 11 | public class OperatingSystemConfiguration : IEnumerable 12 | { 13 | private const string AppName = "OperatingSystemConfiguration"; 14 | private static List _oslist = new List(); 15 | /// 16 | /// Setting the value property 17 | /// 18 | /// 19 | public OperatingSystemConfiguration(string value) { Value = value; } 20 | /// 21 | /// The version number for the given instance. 22 | /// 23 | public string Value { get; set; } 24 | /// 25 | /// Windows 2000 version number 26 | /// 27 | public static OperatingSystemConfiguration Windows2000 { get { return new OperatingSystemConfiguration("5.0"); } } 28 | /// 29 | /// Windows XP version number 30 | /// 31 | public static OperatingSystemConfiguration WindowsXP { get { return new OperatingSystemConfiguration("5.1"); } } 32 | /// 33 | /// Windows XP 64-bit version number 34 | /// 35 | public static OperatingSystemConfiguration WindowsXP64Bit { get { return new OperatingSystemConfiguration("5.2"); } } 36 | /// 37 | /// Windows 2003 version number 38 | /// 39 | public static OperatingSystemConfiguration Windows2003 { get { return new OperatingSystemConfiguration("5.2"); } } 40 | /// 41 | /// Windows 2003R2 version number 42 | /// 43 | public static OperatingSystemConfiguration Windows2003R2 { get { return new OperatingSystemConfiguration("5.2"); } } 44 | /// 45 | /// Windows Vista version number 46 | /// 47 | public static OperatingSystemConfiguration WindowsVista { get { return new OperatingSystemConfiguration("6.0"); } } 48 | /// 49 | /// Windows Server 2008 version number 50 | /// 51 | public static OperatingSystemConfiguration WindowsServer2008 { get { return new OperatingSystemConfiguration("6.0"); } } 52 | /// 53 | /// Windows Server 2008R2 version number 54 | /// 55 | public static OperatingSystemConfiguration WindowsServer2008R2 { get { return new OperatingSystemConfiguration("6.1"); } } 56 | /// 57 | /// Windows 7 version number 58 | /// 59 | public static OperatingSystemConfiguration Windows7 { get { return new OperatingSystemConfiguration("6.1"); } } 60 | /// 61 | /// Windows Server 2012 version number 62 | /// 63 | public static OperatingSystemConfiguration WindowsServer2012 { get { return new OperatingSystemConfiguration("6.2"); } } 64 | /// 65 | /// Windows 8 version number 66 | /// 67 | public static OperatingSystemConfiguration Windows8 { get { return new OperatingSystemConfiguration("6.2"); } } 68 | /// 69 | /// Windows 8.1 version number 70 | /// 71 | public static OperatingSystemConfiguration Windows81 { get { return new OperatingSystemConfiguration("6.3"); } } 72 | /// 73 | /// Windows Server 2012R2 version number 74 | /// 75 | public static OperatingSystemConfiguration WindowsServer2012R2 { get { return new OperatingSystemConfiguration("6.3"); } } 76 | /// 77 | /// Windows 10 version number 78 | /// 79 | public static OperatingSystemConfiguration Windows10 { get { return new OperatingSystemConfiguration("10.0"); } } 80 | /// 81 | /// Windows Server Technical Preview version number 82 | /// 83 | public static OperatingSystemConfiguration WindowsServerTechnicalPreview { get { return new OperatingSystemConfiguration("10.0"); } } 84 | 85 | /// 86 | /// Overloaded function for generating a list of operating systems 87 | /// 88 | /// 89 | public static ICollection ToList() 90 | { 91 | Logger.Log("Creating list of OSes available for detection", Logger.MessageLevel.Information, AppName); 92 | if (_oslist.FirstOrDefault() == null) 93 | { 94 | _oslist.Add(Windows2000); 95 | _oslist.Add(WindowsXP); 96 | _oslist.Add(WindowsXP64Bit); 97 | _oslist.Add(Windows2003); 98 | _oslist.Add(Windows2003R2); 99 | _oslist.Add(WindowsVista); 100 | _oslist.Add(WindowsServer2008); 101 | _oslist.Add(WindowsServer2008R2); 102 | _oslist.Add(Windows7); 103 | _oslist.Add(WindowsServer2012); 104 | _oslist.Add(Windows8); 105 | _oslist.Add(Windows81); 106 | _oslist.Add(WindowsServer2012R2); 107 | _oslist.Add(Windows10); 108 | _oslist.Add(WindowsServerTechnicalPreview); 109 | 110 | } 111 | return _oslist; 112 | } 113 | /// 114 | /// Referencing list via index value 115 | /// 116 | /// 117 | /// 118 | public OperatingSystemConfiguration this[int index] 119 | { 120 | get { return this[index]; } 121 | set { _oslist.Insert(index, value); } 122 | } 123 | 124 | /// 125 | /// Send back list in context of collections 126 | /// 127 | /// 128 | public IEnumerator GetEnumerator() 129 | { 130 | return _oslist.GetEnumerator(); 131 | } 132 | 133 | /// 134 | /// System.Collections.IEnumerator implementation 135 | /// 136 | /// 137 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 138 | { 139 | return this.GetEnumerator(); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/RegistryHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace Microsoft.VS.ConfigurationManager.Support 6 | { 7 | /// 8 | /// SAM values for each set of commands and locations. 9 | /// 10 | public enum RegistrySAM 11 | { 12 | /// 13 | /// No value assigned 14 | /// 15 | None = 0x0000, 16 | /// 17 | /// Query verb 18 | /// 19 | QueryValue = 0x0001, 20 | /// 21 | /// Set verb 22 | /// 23 | SetValue = 0x0002, 24 | /// 25 | /// Create verb 26 | /// 27 | CreateSubKey = 0x0004, 28 | /// 29 | /// Enumerate verb 30 | /// 31 | EnumerateSubKeys = 0x0008, 32 | /// 33 | /// Notify verb 34 | /// 35 | Notify = 0x0010, 36 | /// 37 | /// Create link verb 38 | /// 39 | CreateLink = 0x0020, 40 | /// 41 | /// 32 bit registry location 42 | /// 43 | WOW64_32Key = 0x0200, 44 | /// 45 | /// 64 bit registry location 46 | /// 47 | WOW64_64Key = 0x0100, 48 | /// 49 | /// 50 | /// 51 | WOW64_Res = 0x0300, 52 | /// 53 | /// Read access permissions 54 | /// 55 | Read = 0x00020019, 56 | /// 57 | /// Write access permissions 58 | /// 59 | Write = 0x00020006, 60 | /// 61 | /// Execute access permissions 62 | /// 63 | Execute = 0x00020019, 64 | /// 65 | /// All access permissions 66 | /// 67 | AllAccess = 0x000f003f 68 | } 69 | 70 | /// 71 | /// Registry hive locations 72 | /// 73 | public static class RegHive 74 | { 75 | /// 76 | /// HKLM 77 | /// 78 | internal static UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u); 79 | /// 80 | /// HKLU 81 | /// 82 | internal static UIntPtr HKEY_CURRENT_USER = new UIntPtr(0x80000001u); 83 | } 84 | 85 | /// 86 | /// Exposing class to read from the registry 87 | /// 88 | public static class RegistryHandler 89 | { 90 | 91 | private const string AppName = "RegistryHandler"; 92 | #region Functions 93 | /// 94 | /// Reading from the 64 bit hive 95 | /// 96 | /// 97 | /// 98 | /// 99 | /// 100 | static public string GetRegistryKey64(UIntPtr inHive, String inKeyName, String inPropertyName) 101 | { 102 | Logger.Log("Reading from 64 bit registry hive", Logger.MessageLevel.Verbose, AppName); 103 | return GetRegKey64(inHive, inKeyName, RegistrySAM.WOW64_64Key, inPropertyName); 104 | } 105 | 106 | /// 107 | /// Reading from the 32 bit hive 108 | /// 109 | /// 110 | /// 111 | /// 112 | /// 113 | static public string GetRegistryKey32(UIntPtr inHive, String inKeyName, String inPropertyName) 114 | { 115 | Logger.Log("Reading from 32 bit registry hive", Logger.MessageLevel.Verbose, AppName); 116 | return GetRegKey64(inHive, inKeyName, RegistrySAM.WOW64_32Key, inPropertyName); 117 | } 118 | 119 | /// 120 | /// Get registry key call to imports 121 | /// 122 | /// 123 | /// 124 | /// 125 | /// 126 | /// 127 | static public string GetRegKey64(UIntPtr inHive, String inKeyName, RegistrySAM in32or64key, String inPropertyName) 128 | { 129 | var hkey = 0; 130 | string Age; 131 | try 132 | { 133 | Logger.Log("Open registry handle", Logger.MessageLevel.Verbose, AppName); 134 | var lResult = NativeMethods.RegOpenKeyEx(inHive, inKeyName, 0, (int)RegistrySAM.QueryValue | (int)in32or64key, out hkey); 135 | if (0 != lResult) return null; 136 | uint lpType = 0; 137 | uint lpcbData = 1024; 138 | var AgeBuffer = new StringBuilder(1024); 139 | Logger.Log("Get value from registry", Logger.MessageLevel.Verbose, AppName); 140 | NativeMethods.RegQueryValueEx(hkey, inPropertyName, 0, ref lpType, AgeBuffer, ref lpcbData); 141 | Age = AgeBuffer.ToString(); 142 | } 143 | finally 144 | { 145 | Logger.Log("Close registry handle", Logger.MessageLevel.Verbose, AppName); 146 | if (0 != hkey) NativeMethods.RegCloseKey(hkey); 147 | } 148 | return Age; 149 | } 150 | #endregion 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/Utility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Security; 8 | using System.ServiceProcess; 9 | 10 | namespace Microsoft.VS.ConfigurationManager.Support 11 | { 12 | /// 13 | /// Functions and values to allow central logging and execution of processes 14 | /// 15 | public class Utility : IDisposable 16 | { 17 | private const string AppName = "Utility"; 18 | 19 | /// 20 | /// Setting service state values 21 | /// 22 | public enum ServiceState 23 | { 24 | /// 25 | /// Do nothing 26 | /// 27 | None, 28 | /// 29 | /// Start the service 30 | /// 31 | Start, 32 | /// 33 | /// Stop the service 34 | /// 35 | Stop 36 | } 37 | /// Set properties as part of instantiation. 38 | public Utility() 39 | { 40 | Initialize(); 41 | } 42 | 43 | private void Initialize() { } 44 | 45 | #region Public Methods 46 | 47 | /// 48 | /// Registry key reading function with call to native functions 49 | /// 50 | /// 51 | /// 52 | /// 53 | public static string ReadRegKey(string path, string findkey) 54 | { 55 | var value = string.Empty; 56 | 57 | try { value = RegistryHandler.GetRegistryKey32(RegHive.HKEY_LOCAL_MACHINE, path, findkey); } 58 | catch (Exception ex) { 59 | Logger.Log(ex, AppName); 60 | value = string.Empty; 61 | } 62 | 63 | return value; 64 | } 65 | 66 | #endregion Public Methods 67 | 68 | #region Internal Methods 69 | 70 | 71 | /// Launches a process and returns the error code. 0 is success. 72 | /// 73 | /// 74 | [SecurityCritical] 75 | public static int ExecuteProcess(string file, string args) 76 | { 77 | Logger.Log(String.Format(CultureInfo.InvariantCulture,"Creating process for {0} with arguments: {1}", file, args), Logger.MessageLevel.Information, AppName); 78 | var p = new Process(); 79 | int exitcode = 1603; 80 | try 81 | { 82 | p.StartInfo.UseShellExecute = false; 83 | p.StartInfo.RedirectStandardOutput = true; 84 | p.StartInfo.FileName = file; 85 | p.StartInfo.Arguments = args; 86 | p.StartInfo.Verb = "runas"; 87 | p.Start(); 88 | p.WaitForExit(); 89 | exitcode = p.ExitCode; 90 | } 91 | finally 92 | { 93 | p.Dispose(); 94 | } 95 | 96 | return exitcode; 97 | } 98 | 99 | /// Stopping and starting services required to uninstall MSUs. 100 | /// 101 | /// 102 | /// 103 | public static bool ServiceAction(string ServiceName, ServiceState status) 104 | { 105 | var serviceaction = false; 106 | 107 | sc.ServiceName = ServiceName; 108 | var check = ServiceControllerStatus.Stopped; 109 | try 110 | { 111 | switch (status) 112 | { 113 | case ServiceState.Stop: 114 | check = ServiceControllerStatus.Stopped; 115 | sc.Stop(); 116 | break; 117 | 118 | case ServiceState.Start: 119 | check = ServiceControllerStatus.Running; 120 | sc.Start(); 121 | break; 122 | } 123 | sc.WaitForStatus(check); 124 | serviceaction = true; 125 | } 126 | catch (InvalidOperationException ex) 127 | { 128 | Logger.Log(ex, AppName); 129 | } 130 | 131 | return serviceaction; 132 | } 133 | 134 | #endregion Internal Methods 135 | 136 | #region Private Fields 137 | 138 | private const string FILETYPE_WIXPDB = "WixPdb"; 139 | private const string FILETYPE_BIN = "BIN"; 140 | private const string WIXBUNDLE = "WixBundle"; 141 | 142 | static private string temp = System.IO.Path.GetTempPath(); 143 | static private ServiceController sc = new ServiceController(); 144 | 145 | #endregion Private Fields 146 | 147 | #region Private Methods 148 | 149 | /// Define where the temp directory is located. 150 | public static string TempDir 151 | { 152 | get { return temp; } 153 | set { temp = TrailingSlash(value); } 154 | } 155 | 156 | private static string TrailingSlash(string dir) 157 | { 158 | if (!dir.EndsWith("\\", StringComparison.OrdinalIgnoreCase)) dir += "\\"; 159 | return dir; 160 | } 161 | 162 | #endregion Private Methods 163 | 164 | #region IDisposable Support 165 | 166 | private bool disposedValue; // To detect redundant calls 167 | 168 | /// Clean up objects explicitly 169 | /// 170 | protected virtual void Dispose(bool disposing) 171 | { 172 | if (!disposedValue) 173 | { 174 | Logger.Log("Disposing of objects", Logger.MessageLevel.Verbose, AppName); 175 | if (disposing) 176 | { 177 | sc.Dispose(); 178 | // Logger.Dispose(); 179 | } 180 | 181 | disposedValue = true; 182 | } 183 | sc.Dispose(); 184 | } 185 | 186 | /// Dispose of resources utilitized by Utility item 187 | public void Dispose() 188 | { 189 | // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 190 | Dispose(true); 191 | // TODO: uncomment the following line if the finalizer is overridden above. 192 | GC.SuppressFinalize(this); 193 | } 194 | 195 | #endregion IDisposable Support 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/VS.ConfigurationManager.Support.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {13C73873-A5ED-42DE-97F0-A3B2D7A1D76F} 9 | Library 10 | Properties 11 | VS.ConfigurationManager.Support 12 | VS.ConfigurationManager.Support 13 | v4.0 14 | 512 15 | 16 | 17 | 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | TRACE;DEBUG;DOTNETFRAMEWORK35; 25 | prompt 26 | 4 27 | ManagedMinimumRules.ruleset 28 | false 29 | false 30 | 31 | 32 | pdbonly 33 | true 34 | bin\Release\ 35 | TRACE;DOTNETFRAMEWORK35; 36 | prompt 37 | 4 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Microsoft 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager.Support/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/Bundle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager.Support; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Security.Permissions; 7 | 8 | namespace Microsoft.VS.ConfigurationManager 9 | { 10 | /// Representation of a Wix bundle that is serializable to disk 11 | [Serializable()] 12 | public class Bundle 13 | { 14 | private const string PackageCacheRegistryPath = @"Software\Policies\Microsoft\WiX\Burn"; 15 | private const string PackageCacheValue = @"Package Cache"; 16 | private const string AppName = "Bundle"; 17 | 18 | // TODO: do these vars need to be static? 19 | private string Temp 20 | { 21 | get 22 | { 23 | return System.IO.Path.GetTempPath(); 24 | } 25 | } 26 | private static string ProgramData 27 | { 28 | get 29 | { 30 | return Environment.GetEnvironmentVariable("ALLUSERSPROFILE"); 31 | } 32 | } 33 | private string LogLocation 34 | { 35 | get 36 | { 37 | return System.IO.Path.Combine(Temp, @"dd_Uninstall"); 38 | } 39 | } 40 | private static string PackageCache 41 | { 42 | get 43 | { 44 | if (!regcheck) // if there is no registry value for package cache, it will return an empty string resulting in all bundles requiring a registry read. 45 | { 46 | cache = String.IsNullOrEmpty(cache) && !regcheck ? Utility.ReadRegKey(PackageCacheRegistryPath, PackageCacheValue.Replace(" ", "")) : cache; 47 | regcheck = true; 48 | } 49 | return String.IsNullOrEmpty(cache) ? System.IO.Path.Combine(ProgramData, PackageCacheValue) : cache; 50 | } 51 | } 52 | private static string cache = string.Empty; 53 | private static bool regcheck; 54 | 55 | private System.Guid bundleid; 56 | 57 | // TODO: Refactor constructors to use defaults. 58 | 59 | /// Bundle creation forcing variables to be set. 60 | public Bundle() 61 | { 62 | SetObjectVariables(); 63 | } 64 | 65 | /// Bundle creation with values being passed in before hand. 66 | /// 67 | /// 68 | /// 69 | public Bundle(System.Guid bundleid, string name, string version) 70 | { 71 | Initialize(bundleid, name, version); 72 | } 73 | 74 | /// Bundle creation for all parameters being passed in. 75 | /// 76 | /// 77 | /// 78 | /// 79 | /// 80 | /// 81 | /// 82 | /// 83 | public Bundle(System.Guid bundleid, string name, string version, string releasepdb, string path, string filetype, bool selected, ICollection packages) 84 | { 85 | Initialize(bundleid, name, version); 86 | FileType = filetype; 87 | Selected = selected; 88 | } 89 | /// 90 | /// Creating a bundle without passing package information 91 | /// 92 | /// 93 | /// 94 | /// 95 | /// 96 | /// 97 | /// 98 | /// 99 | public Bundle(System.Guid bundleid, string name, string version, string releasepdb, string path, string filetype, bool selected) 100 | { 101 | Initialize(bundleid, name, version); 102 | FileType = filetype; 103 | Selected = selected; 104 | } 105 | 106 | private void Initialize(System.Guid passedbundleid, string name, string version) 107 | { 108 | SetObjectVariables(); 109 | BundleId = passedbundleid; 110 | Name = name; 111 | Version = version; 112 | _installed = Directory.Exists(LocalInstallLocation) ? true : false; 113 | } 114 | 115 | // TODO: does this need to be static. 116 | static private void SetObjectVariables() 117 | { 118 | } 119 | 120 | /// 121 | /// Override the command line for the bundle installation. The last parameter needs to be 122 | /// the log command line parameter. 123 | /// 124 | public string BundleUninstallArguments 125 | { 126 | get { return "/uninstall /force /Q /Log \"{0}\""; } 127 | } 128 | /// Location of the package cache on disk 129 | public string LocalInstallLocation { get; set; } 130 | 131 | /// Bundle product code 132 | public System.Guid BundleId 133 | { 134 | get { return bundleid; } 135 | set 136 | { 137 | bundleid = value; 138 | LocalInstallLocation = System.IO.Path.Combine(PackageCache, '{' + BundleId.ToString() + '}'); 139 | } 140 | } 141 | 142 | /// WiX Bundle Product Name 143 | public string Name { get; set; } 144 | 145 | /// WiX Bundle Version 146 | public string Version { get; set; } 147 | 148 | internal string FileType { get; set; } 149 | 150 | /// Is the bundle installed? 151 | public bool Installed 152 | { 153 | get { 154 | LocalInstallLocation = System.IO.Path.Combine(PackageCache, '{' + BundleId.ToString() + '}'); 155 | return Directory.Exists(LocalInstallLocation) ? true : false; 156 | } 157 | } 158 | 159 | /// Has the user selected this item for uninstall? 160 | public bool Selected { get; set; } 161 | 162 | private bool _installed; 163 | 164 | /// 165 | /// Initiating an uninstall from the bundle to ensure that force uninstall is first run. 166 | /// Once that has been completed, there can be MSIs that are left behind. 167 | /// 168 | /// 169 | [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust", Unrestricted = false)] 170 | public int Uninstall() 171 | { 172 | var exitcode = -1; 173 | try 174 | { 175 | Logger.Log("Bundle uninstall started.", Logger.MessageLevel.Information, AppName); 176 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "this.Installed: {0}", this.Installed), Logger.MessageLevel.Information, AppName); 177 | if (this.Installed) 178 | { 179 | Logger.Log("Bundle uninstall called and bundle is installed.", Logger.MessageLevel.Information, AppName); 180 | foreach (string file in Directory.GetFiles(LocalInstallLocation, "*.exe")) 181 | { 182 | var bundlelogfilename = LogLocation + "_" + System.IO.Path.ChangeExtension(System.IO.Path.GetFileNameWithoutExtension(file), "log"); 183 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Installer: {0}", file), Logger.MessageLevel.Information, AppName); 184 | var args = String.Format(CultureInfo.InvariantCulture, BundleUninstallArguments, bundlelogfilename); 185 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Arguments: {0}", args), Logger.MessageLevel.Information, AppName); 186 | 187 | Logger.LogWithOutput(string.Format("Uninstalling: {0}", file)); 188 | exitcode = Utility.ExecuteProcess(file, args); 189 | if (exitcode == 0) 190 | Logger.Log("Uninstall succeeded"); 191 | else 192 | { 193 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Uninstall failed with error code: {0}", exitcode), Logger.MessageLevel.Warning, AppName); 194 | break; 195 | } 196 | } 197 | } 198 | } 199 | catch (Exception ex) 200 | { 201 | Logger.Log(ex); 202 | } 203 | 204 | Logger.Log("Bundle uninstall ended.", Logger.MessageLevel.Information, AppName); 205 | 206 | return exitcode; 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/BundlesAndPackagesStore.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Microsoft.VS.ConfigurationManager 8 | { 9 | /// 10 | /// Storing and loading list of bundles and packages. 11 | /// 12 | [Serializable()] 13 | public class BundlesAndPackagesStore 14 | { 15 | /// 16 | /// HashSet of UpgradeCode; we should use UpgradeCode to do package search. 17 | /// 18 | public HashSet UpgradeCodeHash { get; set; } 19 | 20 | /// 21 | /// HashSet of ProductCode; we should use ProductCode to do package search if there's no UpgradeCode is set. 22 | /// 23 | public HashSet NoUpgradeCodeProductCodeHash { get; set; } 24 | 25 | /// 26 | /// A list of bundles. 27 | /// 28 | public List Bundles 29 | { 30 | get; set; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/External/Microsoft.Deployment.WindowsInstaller.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/src/VS.ConfigurationManager/External/Microsoft.Deployment.WindowsInstaller.dll -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/External/wix.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/VisualStudioUninstaller/0444aa51bd221c67432a426ccf171ce329d6bf39/src/VS.ConfigurationManager/External/wix.dll -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/Filter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager.Support; 2 | using System; 3 | using System.Globalization; 4 | 5 | namespace Microsoft.VS.ConfigurationManager 6 | { 7 | /// 8 | /// This class supports search and replace of strings in bundles. It helps in shortening 9 | /// text for easier readability. 10 | /// 11 | public class Filter 12 | { 13 | private const string AppName = "Filter"; 14 | #region Public Methods 15 | /// 16 | /// Create a filter class object with parameters added. 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | public static Filter CreateFilter(string name, string source, string target) 23 | { 24 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Creating Filter - name: {0}, source: {1}, target: {2}", name, source, target), Logger.MessageLevel.Information, AppName); 25 | var filter = new Filter 26 | { 27 | Name = name, 28 | ReplaceSource = source, 29 | ReplaceValue = target 30 | }; 31 | 32 | return filter; 33 | } 34 | #endregion Public Methods 35 | #region Public Properties 36 | /// 37 | /// Name/description of what this text filter does. 38 | /// 39 | public string Name { get; set; } 40 | /// 41 | /// What would you like to search for to replace? 42 | /// 43 | public string ReplaceSource { get; set; } 44 | /// 45 | /// What are you going to replace the ReplaceSource with? 46 | /// 47 | public string ReplaceValue { get; set; } 48 | 49 | #endregion Public Properties 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/Package.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager.Support; 2 | using System; 3 | using System.Globalization; 4 | using System.Security.Permissions; 5 | 6 | 7 | namespace Microsoft.VS.ConfigurationManager 8 | { 9 | /// 10 | /// Representation of a WiX MSI with pertinent information for running an uninstall. 11 | /// 12 | [Serializable()] 13 | public class Package 14 | { 15 | private const string AppName = "Package"; 16 | 17 | static private string systemdir; 18 | static private string temp; 19 | static private string LogLocation; 20 | 21 | /// 22 | /// Definition of what type of Package will be set. Uninstall directives will be different for each. 23 | /// 24 | public enum PackageType 25 | { 26 | /// 27 | /// MSI type 28 | /// 29 | MSI, 30 | /// 31 | /// MSU type 32 | /// 33 | MSU, 34 | /// 35 | /// Redistributables or other bundles 36 | /// 37 | EXE 38 | } 39 | #region Public Properties 40 | 41 | /// 42 | /// When was the MSI installed 43 | /// 44 | public DateTime InstallDate { get; set; } 45 | /// 46 | /// Location of file that initiated the installation 47 | /// 48 | public string InstallLocation { get; set; } 49 | /// 50 | /// WiX MSI product code 51 | /// 52 | public String ProductCode { get; set; } 53 | /// 54 | /// WiX MSI upgrade code 55 | /// 56 | public String UpgradeCode { get; set; } 57 | /// 58 | /// Wix MSI Product Name 59 | /// 60 | public string ProductName { get; set; } 61 | /// 62 | /// Wix MSI Product Version 63 | /// 64 | public string ProductVersion { get; set; } 65 | /// 66 | /// Wix MSI URL value in the case of download 67 | /// 68 | public System.Uri Url { get; set; } 69 | /// 70 | /// Wix MSI Chaining Package value 71 | /// 72 | public string ChainingPackage { get; set; } 73 | /// 74 | /// What set of uninstall instructions should be used is defined by the type of installer that was provided. 75 | /// 76 | public PackageType Type { get; set; } 77 | 78 | private string msiuninstallarguments = @"/qn /norestart IGNOREDEPENDENCIES=ALL "; 79 | private const string msiEXEname = @"msiexec.exe"; 80 | private string msuuninstallarguments = @"/quiet /norestart /uninstall /kb:"; 81 | private const string MSUEXEname = @"wusa.exe"; 82 | 83 | #endregion Public Properties 84 | 85 | #region Public Constructors 86 | /// 87 | /// Constructor to initialize variables necessary for the rest of configuration 88 | /// 89 | public Package() 90 | { 91 | Initialize(); 92 | } 93 | 94 | /// 95 | /// Passing in all fields on creation of an instance 96 | /// 97 | /// 98 | /// 99 | /// 100 | /// 101 | /// 102 | /// 103 | /// 104 | /// 105 | public Package(string upgradecode, string productcode, string productversion, string productname, string chainingpackage) 106 | { 107 | Initialize(); 108 | Type = PackageType.MSI; 109 | UpgradeCode = upgradecode; 110 | ProductCode = productcode; 111 | ProductVersion = productversion; 112 | ProductName = productname; 113 | ChainingPackage = chainingpackage; 114 | } 115 | 116 | /// 117 | /// Passing in all fields on creation of an instance 118 | /// 119 | /// 120 | /// 121 | /// 122 | /// 123 | /// 124 | /// 125 | /// 126 | public Package(string productcode, string productversion, string productname, string chainingpackage) 127 | { 128 | Initialize(); 129 | Type = PackageType.MSI; 130 | ProductCode = productcode; 131 | ProductVersion = productversion; 132 | ProductName = productname; 133 | ChainingPackage = chainingpackage; 134 | } 135 | /// 136 | /// Overloaded value to allow a different value for PackageType to be set. 137 | /// 138 | /// 139 | /// 140 | /// 141 | /// 142 | /// 143 | /// 144 | /// 145 | /// 146 | public Package(string productcode, string productversion, string productname, string chainingpackage, PackageType type) 147 | { 148 | Initialize(); 149 | Type = type; 150 | ProductCode = productcode; 151 | ProductVersion = productversion; 152 | ProductName = productname; 153 | ChainingPackage = chainingpackage; 154 | } 155 | #endregion Public Constructors 156 | 157 | #region Public Methods 158 | /// 159 | /// Override for the MSI uninstall command line installation. 160 | /// - /x command is already in place. 161 | /// - /L*V command is already in place. 162 | /// 163 | public string MSUUninstallArguments 164 | { 165 | get { return msuuninstallarguments; } 166 | set { msuuninstallarguments = value.TrimEnd() + " "; } 167 | } 168 | 169 | /// 170 | /// Override for the MSI uninstall command line installation. 171 | /// - /x command is already in place. 172 | /// - /L*V command is already in place. 173 | /// 174 | public string MSIUninstallArguments 175 | { 176 | get { return msiuninstallarguments; } 177 | set { msiuninstallarguments = value.TrimEnd() + " "; } 178 | } 179 | /// 180 | /// Returns the product name of this instantiation 181 | /// 182 | /// 183 | public override string ToString() 184 | { 185 | return ProductName; 186 | } 187 | 188 | /// 189 | /// Initiating an uninstall from the MSI to ensure that force uninstall is first run. 190 | /// Once that has been completed, there can be MSIs that are left behind. 191 | /// 192 | /// 193 | 194 | [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust", Unrestricted = false)] 195 | public int Uninstall() 196 | { 197 | var exitcode = -1; 198 | var args = string.Empty; 199 | var file = string.Empty; 200 | switch (this.Type) 201 | { 202 | case PackageType.MSI: 203 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Installer: {0}", this.ProductName), Logger.MessageLevel.Information, AppName); 204 | var msilogfilename = LogLocation + "_" + System.IO.Path.ChangeExtension(this.NormalizeProductName(this.ProductName), "log"); 205 | // Run msiexec from the system path only. 206 | file = System.IO.Path.Combine(systemdir, msiEXEname); 207 | // Quiet uninstall with no restart requested and logging enabled 208 | args = String.Format(CultureInfo.InvariantCulture, MSIUninstallArguments + "/x {0} /L*v \"{1}\"", this.ProductCode.ToString(), msilogfilename); 209 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Arguments: {0}", args)); 210 | 211 | exitcode = Utility.ExecuteProcess(file, args); 212 | if (exitcode == 0) 213 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSI [{0}] Uninstall succeeded", this.ProductName), Logger.MessageLevel.Information, AppName); 214 | else 215 | { 216 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSI [{0}] Uninstall failed with error code: {1}", this.ProductName, exitcode), Logger.MessageLevel.Information, AppName); 217 | } 218 | break; 219 | case PackageType.MSU: 220 | Utility.ServiceAction("wuauserv", Utility.ServiceState.Stop); 221 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "KB{0} - {1}", this.ProductCode, this.ProductName), Logger.MessageLevel.Information, AppName); 222 | args = String.Format(CultureInfo.InvariantCulture, msuuninstallarguments.TrimEnd() + "{0}", this.ProductCode); 223 | file = System.IO.Path.Combine(systemdir, MSUEXEname); 224 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Arguments: {0}", args), Logger.MessageLevel.Information, AppName); 225 | exitcode = Utility.ExecuteProcess(file, args); 226 | if ( 227 | (exitcode == 0) || // success 228 | (exitcode == 3010) || // reboot required 229 | (exitcode == 2359303) // already uninstalled 230 | ) 231 | { 232 | var msg = String.Empty; 233 | switch (exitcode) 234 | { 235 | case 3010: 236 | msg = "(Reboot required)"; 237 | break; 238 | case 2359303: 239 | exitcode = 0; // Override error message from the MSU to signal success 240 | msg = "(Previously uninstalled)"; 241 | break; 242 | } 243 | 244 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSU [KB{0}] Uninstall succeeded {1}", this.ProductCode, msg), Logger.MessageLevel.Information, AppName); 245 | } 246 | else 247 | { 248 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSU [KB{0}] Uninstall failed with error code: {1}", this.ProductCode, exitcode), Logger.MessageLevel.Information, AppName); 249 | } 250 | Utility.ServiceAction("wuauserv", Utility.ServiceState.Stop); 251 | break; 252 | case PackageType.EXE: 253 | break; 254 | } 255 | return exitcode; 256 | } 257 | 258 | private string NormalizeProductName(string productName) 259 | { 260 | return productName.Replace(" ", string.Empty).Replace("/", string.Empty); 261 | } 262 | 263 | #endregion Public Methods 264 | 265 | static private void Initialize() 266 | { 267 | systemdir = Environment.SystemDirectory; 268 | temp = System.IO.Path.GetTempPath(); 269 | LogLocation = System.IO.Path.Combine(temp, @"dd_Uninstall"); 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/Primitives.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager.Support; 2 | using Microsoft.Deployment.WindowsInstaller; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Security.Permissions; 9 | using System.Text; 10 | using Wix = Microsoft.Tools.WindowsInstallerXml; 11 | 12 | namespace Microsoft.VS.ConfigurationManager 13 | { 14 | /// 15 | /// Publically available actions for handling uninstall actions 16 | /// 17 | public class Primitives : IDisposable 18 | { 19 | 20 | #region Public Properties 21 | /// 22 | /// Current machine OS version 23 | /// 24 | public string MachineOSVersion { get; set; } 25 | /// 26 | /// Current machine architecture 27 | /// 28 | public ArchitectureConfiguration MachineArchitectureConfiguration { get; set; } 29 | 30 | public BundlesAndPackagesStore BundlesAndPackagesStore { get; set; } 31 | 32 | /// 33 | /// Flag for ensuring data is loaded successfully. 34 | /// 35 | public bool Processed { get; set; } 36 | 37 | /// 38 | /// Used to debug on development machine. Prevents execution of bundle uninstall and 39 | /// msi uninstalls. 40 | /// 41 | public bool DoNotExecuteProcess { get; set; } 42 | 43 | /// 44 | /// The location where you want files written to when creating output files. 45 | /// 46 | public string DataFilesPath { get; set; } 47 | 48 | /// 49 | /// string of releases that can be passed in to initiate an uninstall for. 50 | /// 51 | public bool DebugReporting 52 | { 53 | get { return debugreporting; } 54 | set 55 | { 56 | debugreporting = value; 57 | Logger.Debug = value; 58 | } 59 | } 60 | /// 61 | /// A set of uninstall actions that will be done either before or after the main uninstall process 62 | /// 63 | public ICollection UninstallActions 64 | { 65 | get { return uninstallactions; } 66 | } 67 | /// 68 | /// List of filters (search and replace) to be applied to text 69 | /// 70 | public ICollection Filters 71 | { 72 | get { return filters; } 73 | } 74 | 75 | /// 76 | /// Accepts a List of Filter (class) object that provides search and replace on for 77 | /// text in product and msi names. Helps shorten output. Progress 78 | /// indicator value when running an uninstall 79 | /// 80 | public decimal Progress { get; set; } 81 | 82 | /// 83 | /// Provides a string that reports on the releases supported and selected 84 | /// 85 | public string ReleaseOutput { get; set; } 86 | 87 | #endregion Public Properties 88 | 89 | #region Public Methods 90 | 91 | /// 92 | /// Constructor. 93 | /// 94 | public Primitives() 95 | { 96 | this.BundlesAndPackagesStore = new BundlesAndPackagesStore(); 97 | this.BundlesAndPackagesStore.UpgradeCodeHash = new HashSet(); 98 | this.BundlesAndPackagesStore.NoUpgradeCodeProductCodeHash = new HashSet(); 99 | this.BundlesAndPackagesStore.Bundles = new List(); 100 | } 101 | 102 | /// 103 | /// Using the List of Filter classes, do a replace of strings per the user's 104 | /// specification. 105 | /// 106 | /// 107 | /// 108 | public string ApplyFilter(string Source) 109 | { 110 | foreach (Filter fil in filters) { Source = Source.Replace(fil.ReplaceSource, fil.ReplaceValue); } 111 | return Source; 112 | } 113 | 114 | /// 115 | /// Explicit disposal of objects 116 | /// 117 | public void Dispose() 118 | { 119 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Disposing of {0}", AppName), Logger.MessageLevel.Verbose, AppName); 120 | Dispose(true); 121 | GC.SuppressFinalize(this); 122 | ut.Dispose(); 123 | } 124 | 125 | /// 126 | /// GetInstalledItems lists all items that are installed on this machine. 127 | /// 128 | /// 129 | public ICollection GetAllInstalledItems() 130 | { 131 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Getting all installed items", AppName), Logger.MessageLevel.Information, AppName); 132 | ICollection installations = new List(); 133 | 134 | try 135 | { 136 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Do we already have an object in memory?", AppName), Logger.MessageLevel.Verbose, AppName); 137 | if (installedmsis.FirstOrDefault() == null) 138 | { 139 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "No installpackages object found - creating", AppName), Logger.MessageLevel.Verbose, AppName); 140 | 141 | installations = ProductInstallation.GetProducts(null, null, UserContexts.All) 142 | .Select(ins => new Package( 143 | this.GetUpgradeCode(ins.LocalPackage), 144 | ins.ProductCode, 145 | ins.ProductVersion == null ? "0.0.0" : ins.ProductVersion.ToString(), 146 | ApplyFilter(string.IsNullOrEmpty(ins.ProductName) ? "(NOTDEFINED)" : ins.ProductName), 147 | null 148 | ) 149 | ) 150 | .OrderBy(ins => ins.ProductName).ToList(); 151 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Packages installed: {0}", installations.Count().ToString(CultureInfo.InvariantCulture))); 152 | } 153 | else 154 | { 155 | Logger.Log("installedpackages is populated."); 156 | installations = (List)installedmsis; 157 | } 158 | } 159 | catch (Exception ex) 160 | { 161 | Logger.Log(ex.Message, Logger.MessageLevel.Error, AppName); 162 | } 163 | installedmsis = installations; 164 | return installations; 165 | } 166 | 167 | /// 168 | /// Clean up Visual Studio folders. 169 | /// 170 | /// 171 | public void CleanupVisualStudioFolders(IEnumerable vsInstallPaths) 172 | { 173 | foreach (var path in vsInstallPaths) 174 | { 175 | try 176 | { 177 | if (!string.IsNullOrEmpty(path) && Directory.Exists(path) && !this.DoNotExecuteProcess) 178 | { 179 | Logger.LogWithOutput(string.Format("Deleting: {0}", path)); 180 | this.RecursivelyDeleteFolder(path); 181 | } 182 | } 183 | catch (Exception ex) 184 | { 185 | Logger.LogWithOutput(string.Format("Cannot delete Secondary Installer cache with error: {0}", ex.Message)); 186 | } 187 | } 188 | } 189 | 190 | /// 191 | /// Clean up HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio 192 | /// Clean up HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio 193 | /// 194 | public void CleanupVisualStudioRegistryHives() 195 | { 196 | var keyPaths = new string[] { 197 | @"SOFTWARE\Microsoft\VisualStudio\12.0", 198 | @"SOFTWARE\Microsoft\VisualStudio\14.0", 199 | @"SOFTWARE\Microsoft\VisualStudio\15.0", 200 | @"SOFTWARE\Microsoft\VisualStudio\12.0_Config", 201 | @"SOFTWARE\Microsoft\VisualStudio\14.0_Config", 202 | @"SOFTWARE\Microsoft\VisualStudio\15.0_Config", 203 | @"SOFTWARE\Microsoft\DevDiv\vs\Servicing\12.0", 204 | @"SOFTWARE\Microsoft\DevDiv\vs\Servicing\14.0", 205 | @"SOFTWARE\Microsoft\DevDiv\vs\Servicing\15.0", 206 | }; 207 | 208 | foreach(var keyPath in keyPaths) 209 | { 210 | if (!this.DoNotExecuteProcess) 211 | { 212 | Logger.LogWithOutput(string.Format("Deleting registry: {0}", keyPath)); 213 | this.DeleteRegistryKey(keyPath); 214 | } 215 | } 216 | 217 | } 218 | 219 | private void DeleteRegistryKey(string keyPath) 220 | { 221 | try 222 | { 223 | var x86View = Win32.RegistryKey.OpenBaseKey(Win32.RegistryHive.LocalMachine, Win32.RegistryView.Registry32); 224 | x86View.DeleteSubKeyTree(keyPath, false); 225 | 226 | var x64View = Win32.RegistryKey.OpenBaseKey(Win32.RegistryHive.LocalMachine, Win32.RegistryView.Registry64); 227 | x64View.DeleteSubKeyTree(keyPath, false); 228 | 229 | x86View = Win32.RegistryKey.OpenBaseKey(Win32.RegistryHive.CurrentUser, Win32.RegistryView.Registry32); 230 | x86View.DeleteSubKeyTree(keyPath, false); 231 | 232 | x64View = Win32.RegistryKey.OpenBaseKey(Win32.RegistryHive.CurrentUser, Win32.RegistryView.Registry64); 233 | x64View.DeleteSubKeyTree(keyPath, false); 234 | } 235 | catch (Exception ex) 236 | { 237 | Logger.Log(string.Format("Cannot delete registry with error: {0}", ex.Message)); 238 | } 239 | } 240 | 241 | /// 242 | /// Cleanup %ProgramData%\Microsoft\VisualStudioSecondaryInstaller 243 | /// 244 | public void CleanupSecondaryInstallerCache() 245 | { 246 | try 247 | { 248 | if (Directory.Exists(CommonApplicationDataDirectory) && !this.DoNotExecuteProcess) 249 | { 250 | Logger.LogWithOutput(string.Format("Deleting: {0}", CommonApplicationDataDirectory)); 251 | this.RecursivelyDeleteFolder(CommonApplicationDataDirectory); 252 | } 253 | } 254 | catch (Exception ex) 255 | { 256 | Logger.LogWithOutput(string.Format("Cannot delete Secondary Installer cache with error: {0}", ex.Message)); 257 | } 258 | } 259 | 260 | private static string CommonApplicationDataDirectory 261 | { 262 | get 263 | { 264 | return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), 265 | Path.Combine(@"Microsoft", "VisualStudioSecondaryInstaller")); 266 | } 267 | } 268 | 269 | /// 270 | /// delete a folder and all its content 271 | /// 272 | /// 273 | private void RecursivelyDeleteFolder(string folder) 274 | { 275 | foreach (string subDirectory in Directory.GetDirectories(folder)) 276 | { 277 | RecursivelyDeleteFolder(subDirectory); 278 | } 279 | 280 | foreach (string file in Directory.GetFiles(folder)) 281 | { 282 | DeleteFileIfExists(file); 283 | } 284 | 285 | Directory.Delete(folder); 286 | } 287 | 288 | 289 | private void DeleteFileIfExists(string filePath) 290 | { 291 | if (string.IsNullOrEmpty(filePath)) 292 | { 293 | throw new ArgumentNullException("filePath"); 294 | } 295 | 296 | if (File.Exists(filePath)) 297 | { 298 | try 299 | { 300 | File.Delete(filePath); 301 | } 302 | catch (IOException) // The specified file is in use. -or- ... 303 | { 304 | System.Threading.Thread.Sleep(500); 305 | File.Delete(filePath); 306 | } 307 | catch (UnauthorizedAccessException) 308 | { 309 | // see if it was because file is read only 310 | System.IO.FileAttributes copiedFileAttributes = File.GetAttributes(filePath); 311 | if ((copiedFileAttributes & System.IO.FileAttributes.ReadOnly).Equals(System.IO.FileAttributes.ReadOnly)) 312 | { 313 | // remove read only flag 314 | File.SetAttributes(filePath, copiedFileAttributes & ~System.IO.FileAttributes.ReadOnly); 315 | 316 | // try again 317 | File.Delete(filePath); 318 | } 319 | } 320 | } 321 | } 322 | 323 | private string GetUpgradeCode(string installSource) 324 | { 325 | if (File.Exists(installSource)) 326 | { 327 | try 328 | { 329 | using (var database = new Database(installSource, DatabaseOpenMode.ReadOnly)) 330 | { 331 | using (var view = database.OpenView(database.Tables["Property"].SqlSelectString)) 332 | { 333 | view.Execute(); 334 | foreach (var rec in view) 335 | { 336 | if ("UpgradeCode".Equals(rec.GetString("Property"), StringComparison.OrdinalIgnoreCase)) 337 | { 338 | return rec.GetString("Value"); 339 | } 340 | } 341 | } 342 | } 343 | } 344 | catch(Exception e) 345 | { 346 | Logger.Log(e); 347 | } 348 | } 349 | else 350 | { 351 | Logger.Log(string.Format("The {0} doesn't exist, cannot find upgrade code.", installSource)); 352 | } 353 | 354 | return string.Empty; 355 | } 356 | 357 | /// 358 | /// Locate PDB files on disk and store them in the release object 359 | /// 360 | public void Initialize() 361 | { 362 | try 363 | { 364 | // Get all files that are in the content directory. Record them as a Bundle for 365 | // later usage. Additionally, check the install state of each Bundle. 366 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Initialize called successfully."), Logger.MessageLevel.Information, AppName); 367 | Processed = false; 368 | } 369 | catch (Exception ex) 370 | { 371 | Logger.Log(ex.Message, Logger.MessageLevel.Error, AppName); 372 | } 373 | } 374 | 375 | /// 376 | /// Load from a data file. 377 | /// 378 | /// 379 | public void LoadFromDataFile(string path) 380 | { 381 | // Generate file name based on configuration and name of the wixpdb 382 | long position = 0; 383 | // create a new formatter instance 384 | var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 385 | 386 | using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) 387 | { 388 | if (position < stream.Length) 389 | { 390 | stream.Seek(position, SeekOrigin.Begin); 391 | this.BundlesAndPackagesStore = (BundlesAndPackagesStore)formatter.Deserialize(stream); 392 | position = stream.Position; 393 | } 394 | } 395 | formatter = null; 396 | } 397 | 398 | /// 399 | /// Load from a data file stream. 400 | /// 401 | /// 402 | public void LoadFromDataFile(Stream stream) 403 | { 404 | // Generate file name based on configuration and name of the wixpdb 405 | long position = 0; 406 | // create a new formatter instance 407 | var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 408 | 409 | if (position < stream.Length) 410 | { 411 | stream.Seek(position, SeekOrigin.Begin); 412 | this.BundlesAndPackagesStore = (BundlesAndPackagesStore)formatter.Deserialize(stream); 413 | position = stream.Position; 414 | } 415 | formatter = null; 416 | } 417 | 418 | /// 419 | /// 420 | /// Given a file that has been serialized out, this will read in the file and 421 | /// hydrate the object model for a list of InstallableItem. 422 | /// 423 | /// 424 | /// It specifically takes a directory to read all BIN files associated. If an 425 | /// invalid BIN file is found, it will report an error and the loop will continue. 426 | /// 427 | /// 428 | /// 429 | /// 430 | public Bundle LoadFromFile(string value) 431 | { 432 | var outObj = FileToBundle(value); 433 | Processed = true; 434 | return outObj; 435 | } 436 | 437 | /// 438 | /// 439 | /// Takes a list of InstallableItem and converts it to binary output. 440 | /// 441 | /// 442 | /// This is used after data is extracted from a wixpdb using 443 | /// Primitive.GetDataFromPdb() 444 | /// 445 | /// 446 | /// 447 | public void Save(ICollection installable) 448 | { 449 | if (Processed) 450 | { 451 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "File path: {0} Files saved: {1}", this.DataFilesPath, installable.Count().ToString(CultureInfo.InvariantCulture))); 452 | BundlesToFiles(installable, this.DataFilesPath, FILETYPE_BIN); 453 | } 454 | else 455 | { 456 | Logger.Log("WixPdbs have not been processed or the configuration files have not been loaded. Nothing to save.", Logger.MessageLevel.Warning, AppName); 457 | } 458 | } 459 | 460 | /// 461 | /// Save BundlesAndPackageStore object to a data file. 462 | /// 463 | public void SaveToDataFile() 464 | { 465 | // create a new formatter instance 466 | var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 467 | 468 | string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); 469 | Directory.CreateDirectory(tempDirectory); 470 | 471 | string fileName = Path.Combine(tempDirectory, "DataFile.bin"); 472 | 473 | Console.WriteLine(@"Writing data file to " + fileName); 474 | 475 | try 476 | { 477 | // open a filestream 478 | using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write)) 479 | { 480 | formatter.Serialize(stream, this.BundlesAndPackagesStore); 481 | } 482 | }catch(Exception e) 483 | { 484 | Console.WriteLine(@"Failed to write data file to " + fileName + " reason: " + e.Message); 485 | } 486 | 487 | Console.WriteLine(string.Format("Writing data file to {0}, completed. ", fileName)); 488 | } 489 | 490 | /// 491 | /// Uninstall a specific WiX MSI 492 | /// 493 | /// 494 | /// 495 | [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust", Unrestricted = false)] 496 | public int Uninstall(Package package) 497 | { 498 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Package uninstall initiated: {0}", package.ProductName), Logger.MessageLevel.Information, AppName); 499 | var exitcode = -1; 500 | 501 | try 502 | { 503 | if (!DoNotExecuteProcess) 504 | { 505 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Package uninstall method called"), Logger.MessageLevel.Verbose, AppName); 506 | exitcode = package.Uninstall(); 507 | } 508 | else 509 | { 510 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Package uninstall method bypassed - DoNotExecute is true"), Logger.MessageLevel.Verbose, AppName); 511 | exitcode = 0; 512 | } 513 | } 514 | catch (Exception ex) 515 | { 516 | Logger.Log(ex, AppName); 517 | } 518 | 519 | return exitcode; 520 | } 521 | 522 | /// 523 | /// Report what Visual Studio's were installed on this system. 524 | /// 525 | public void InstalledVisualStudioReport() 526 | { 527 | List installedBundles = new List(this.BundlesAndPackagesStore.Bundles.Where(b => b.Installed)); 528 | var installedBundleStrings = installedBundles.Select(b => 529 | String.Format("(Name: {0}, Version: {1}, BundleId: {2})", b.Name, b.Version, b.BundleId)).ToArray(); 530 | 531 | if (installedBundleStrings.Count() > 0) 532 | { 533 | Logger.LogWithOutput(string.Format(@"The following bundles were detected on your system: "), Logger.MessageLevel.Information, AppName); 534 | 535 | foreach (var ib in installedBundleStrings) 536 | { 537 | Logger.LogWithOutput(string.Format(ib), Logger.MessageLevel.Information, AppName); 538 | } 539 | } 540 | else 541 | { 542 | Logger.LogWithOutput(string.Format(@"No bundle found. Uninstalling stale MSIs. "), Logger.MessageLevel.Information, AppName); 543 | } 544 | } 545 | 546 | /// 547 | /// Uninstall Visual Studio 2013/2015/vNext 548 | /// 549 | public int Uninstall() 550 | { 551 | List installedBundles = new List(this.BundlesAndPackagesStore.Bundles.Where(b => b.Installed)); 552 | 553 | List orderedBundles = new List(); 554 | 555 | foreach (var ib in installedBundles) 556 | { 557 | if (!ib.Name.ToLowerInvariant().Contains(@"(kb")) 558 | { 559 | orderedBundles.Add(ib); 560 | } 561 | } 562 | 563 | foreach (var ib in installedBundles) 564 | { 565 | if (ib.Name.ToLowerInvariant().Contains(@"(kb")) 566 | { 567 | orderedBundles.Add(ib); 568 | } 569 | } 570 | 571 | foreach (var ib in orderedBundles) 572 | { 573 | int exitCode = 0; 574 | if (!this.DoNotExecuteProcess) 575 | { 576 | try 577 | { 578 | exitCode = ib.Uninstall(); 579 | } 580 | catch(Exception ex) 581 | { 582 | Logger.LogWithOutput( 583 | string.Format("Bundle: {0} uninstalled failed with exception: {1}. ", ib.Name, ex.Message)); 584 | } 585 | } 586 | Logger.LogWithOutput(string.Format("Bundle: {0} has been uninstalled with exit code: {1}. ", ib.Name, exitCode)); 587 | 588 | if (exitCode == 3010) 589 | { 590 | return exitCode; 591 | } 592 | } 593 | 594 | Logger.LogWithOutput("Normal Visual Studio Uninstall completed."); 595 | Logger.LogWithOutput("Searching for stale MSIs and clean up stale MSIs."); 596 | 597 | var installedPackages = this.GetAllInstalledItems(); 598 | List packagesToBeUninstalled = new List(); 599 | 600 | foreach(var ip in installedPackages) 601 | { 602 | if (this.BundlesAndPackagesStore.UpgradeCodeHash.Contains(ip.UpgradeCode)) 603 | { 604 | packagesToBeUninstalled.Add(ip); 605 | } 606 | else if (this.BundlesAndPackagesStore.NoUpgradeCodeProductCodeHash.Contains(ip.ProductCode)) 607 | { 608 | packagesToBeUninstalled.Add(ip); 609 | } 610 | } 611 | 612 | if (packagesToBeUninstalled.Count > 0) 613 | { 614 | Logger.LogWithOutput(string.Format("{0} stale MSIs found. Uninstalling them.", packagesToBeUninstalled.Count )); 615 | 616 | int count = packagesToBeUninstalled.Count; 617 | foreach (var p in packagesToBeUninstalled) 618 | { 619 | int rc = 0; 620 | 621 | if (!this.DoNotExecuteProcess) 622 | { 623 | try 624 | { 625 | rc = p.Uninstall(); 626 | } 627 | catch(Exception ex) 628 | { 629 | Logger.LogWithOutput( 630 | string.Format("Msi: {0} uninstalled failed with exception: {1}. ", p.ProductName, ex.Message)); 631 | } 632 | } 633 | count--; 634 | Logger.LogWithOutput(string.Format("Uninstalled {0} with exit code: {1}. {2}/{3}", p.ProductName, rc, count, packagesToBeUninstalled.Count)); 635 | } 636 | } 637 | 638 | return 0; 639 | } 640 | 641 | #endregion Public Methods 642 | 643 | #region Protected Methods 644 | 645 | /// 646 | protected virtual void Dispose(bool disposing) 647 | { 648 | if (!disposed) 649 | { 650 | if (disposing) { 651 | BundlesAndPackagesStore.UpgradeCodeHash = null; 652 | BundlesAndPackagesStore.NoUpgradeCodeProductCodeHash = null; 653 | BundlesAndPackagesStore.Bundles = null; 654 | ut.Dispose(); 655 | } 656 | } 657 | //dispose unmanaged resources 658 | disposed = true; 659 | } 660 | 661 | #endregion Protected Methods 662 | 663 | #region Private Fields 664 | 665 | private const string CHAINMSIPACKAGE = "ChainMsiPackage"; 666 | 667 | private const string UXPACKAGEBEHAVIOR = "UxPackageBehavior"; 668 | 669 | private const string FILETYPE_WIXPDB = "WixPdb"; 670 | 671 | private const string FILETYPE_BIN = "BIN"; 672 | 673 | private const string WIXBUNDLE = "WixBundle"; 674 | 675 | private const string AppName = "Primitives"; 676 | 677 | private bool debugreporting; 678 | 679 | private bool disposed; 680 | 681 | private ICollection filters = new List(); 682 | 683 | private ICollection uninstallactions = new List(); 684 | 685 | private ICollection installedmsis = new List(); 686 | 687 | private string releaseoutput = string.Empty; 688 | 689 | private Utility ut = new Utility(); 690 | 691 | #endregion Private Fields 692 | 693 | #region Private Methods 694 | private void GetUniquePackages(HashSet upgradeCodeHash, 695 | HashSet noUpgradeCodeProductCodeHash, 696 | Wix.Table chainmsipackageTable, 697 | Wix.Table uxPackageBehavior) 698 | { 699 | try 700 | { 701 | Dictionary uxPackageBehaviorDict = new Dictionary(); 702 | if (uxPackageBehavior != null) 703 | { 704 | foreach (Wix.Row msirow in uxPackageBehavior.Rows) 705 | { 706 | string packageId = string.Empty; 707 | string reallyPerm = string.Empty; 708 | foreach (Wix.Field field in msirow.Fields) 709 | { 710 | switch (field.Column.Name.ToString(CultureInfo.InvariantCulture).ToUpperInvariant()) 711 | { 712 | case "PackageId": 713 | packageId = field.Data.ToString(); 714 | break; 715 | case "ReallyPermanent": // nullable. 716 | if (field.Data != null) 717 | { 718 | reallyPerm = field.Data.ToString(); 719 | } 720 | break; 721 | 722 | } 723 | } 724 | if (!string.IsNullOrEmpty(packageId) && !uxPackageBehaviorDict.ContainsKey(packageId)) 725 | { 726 | uxPackageBehaviorDict.Add(packageId, reallyPerm); 727 | } 728 | } 729 | } 730 | 731 | foreach (Wix.Row msirow in chainmsipackageTable.Rows) 732 | { 733 | var msi = new Package(); 734 | 735 | foreach (Wix.Field field in msirow.Fields) 736 | { 737 | switch (field.Column.Name.ToString(CultureInfo.InvariantCulture).ToUpperInvariant()) 738 | { 739 | case "CHAINPACKAGE_": 740 | msi.ChainingPackage = field.Data.ToString(); 741 | break; 742 | 743 | case "PRODUCTCODE": // id 23 744 | msi.ProductCode = field.Data.ToString(); 745 | break; 746 | 747 | case "UPGRADECODE": // nullable. 748 | if (field.Data != null) 749 | { 750 | msi.UpgradeCode = field.Data.ToString(); 751 | } 752 | break; 753 | case "PRODUCTVERSION": 754 | msi.ProductVersion = field.Data.ToString(); 755 | break; 756 | 757 | case "PRODUCTNAME": 758 | msi.ProductName = field.Data.ToString(); 759 | break; 760 | case "PACKAGETYPE": 761 | msi.Type = Package.PackageType.MSI; 762 | break; 763 | default: 764 | break; 765 | } 766 | } 767 | 768 | // if the package is really perm, then, don't uninstall it. 769 | if (!string.IsNullOrEmpty(msi.ChainingPackage) 770 | && uxPackageBehaviorDict.ContainsKey(msi.ChainingPackage) 771 | && uxPackageBehaviorDict[msi.ChainingPackage].Equals("yes", StringComparison.OrdinalIgnoreCase)) 772 | { 773 | continue; 774 | } 775 | 776 | if (string.IsNullOrEmpty(msi.UpgradeCode)) 777 | { 778 | noUpgradeCodeProductCodeHash.Add(msi.ProductCode); 779 | } 780 | else 781 | { 782 | if (!upgradeCodeHash.Contains(msi.UpgradeCode)) 783 | { 784 | upgradeCodeHash.Add(msi.UpgradeCode); 785 | } 786 | } 787 | } 788 | // We should not be uninstalling MSU because they are usually perm and they are windows comp. 789 | } 790 | catch (Exception ex) 791 | { 792 | Logger.Log(ex); 793 | } 794 | } 795 | 796 | static private Bundle FileToBundle(string file) 797 | { 798 | // Generate file name based on configuration and name of the wixpdb 799 | long position = 0; 800 | // create a new formatter instance 801 | var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 802 | 803 | // read the animal as position back 804 | Bundle installable = null; 805 | using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read)) 806 | { 807 | if (position < stream.Length) 808 | { 809 | stream.Seek(position, SeekOrigin.Begin); 810 | installable = (Bundle)formatter.Deserialize(stream); 811 | position = stream.Position; 812 | } 813 | } 814 | formatter = null; 815 | return installable; 816 | } 817 | 818 | /// 819 | /// Load a wixpdb 820 | /// 821 | /// 822 | /// 823 | public bool LoadFromWixpdb(string path) 824 | { 825 | try 826 | { 827 | // Get all files associated with WixPDBs in directory 828 | Logger.Log(String.Format(CultureInfo.InvariantCulture, "Loading {0}", path), Logger.MessageLevel.Information, "Utility"); 829 | var pdb = Wix.Pdb.Load(Path.GetFullPath(path), true, true); 830 | var bundlerowinfo = (Wix.WixBundleRow)GetBundlesFromWixPDB(pdb).Rows[0]; 831 | 832 | var bundle = new Bundle(bundlerowinfo.BundleId, bundlerowinfo.Name, bundlerowinfo.Version, Path.GetFileNameWithoutExtension(path), Path.GetFullPath(path), FILETYPE_WIXPDB, false); 833 | if (!this.BundlesAndPackagesStore.Bundles.Any(b => b.BundleId == bundle.BundleId)) 834 | { 835 | this.BundlesAndPackagesStore.Bundles.Add(bundle); 836 | } 837 | 838 | if (pdb.Output.Type == Wix.OutputType.Bundle) 839 | { 840 | var wixbundle = pdb.Output.Tables[WIXBUNDLE]; //Id: 32 in pdb.Output.Rows 841 | var chainmsipackageTable = pdb.Output.Tables[CHAINMSIPACKAGE]; //Id: 0 in pdb.Output.Rows 842 | var uxPackageBehavior = pdb.Output.Tables[UXPACKAGEBEHAVIOR]; //Id: 0 in pdb.Output.Rows 843 | 844 | if (wixbundle != null) 845 | { 846 | if (chainmsipackageTable != null) 847 | { 848 | this.GetUniquePackages( 849 | this.BundlesAndPackagesStore.UpgradeCodeHash, 850 | this.BundlesAndPackagesStore.NoUpgradeCodeProductCodeHash, 851 | chainmsipackageTable, 852 | uxPackageBehavior); 853 | } 854 | } 855 | } 856 | 857 | bundlerowinfo = null; 858 | } 859 | catch (Exception e) 860 | { 861 | Logger.Log("Unable to load wixpdb: " + path, Logger.MessageLevel.Error); 862 | Logger.Log(e); 863 | return false; 864 | } 865 | return true; 866 | } 867 | 868 | private static Wix.Table GetBundlesFromWixPDB(Microsoft.Tools.WindowsInstallerXml.Pdb pdb) 869 | { 870 | var wixbundle = pdb.Output.Tables[WIXBUNDLE]; //Id: 32 in pdb.Output.Rows 871 | return wixbundle; 872 | } 873 | 874 | private static void BundlesToFiles(ICollection installable, string DataFilesPath, string ext) 875 | { 876 | var path = DataFilesPath ?? System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "DataFiles"); 877 | var di = new DirectoryInfo(path); 878 | 879 | if (!di.Exists) 880 | di.Create(); 881 | 882 | string filename = null; 883 | 884 | foreach (Bundle insitem in installable) 885 | { 886 | // Generate file name based on configuration and name of the wixpdb 887 | filename = System.IO.Path.Combine(path, Path.ChangeExtension(insitem.Name, ext)); 888 | 889 | // create a new formatter instance 890 | var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 891 | 892 | // open a filestream 893 | using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write)) 894 | { 895 | formatter.Serialize(stream, insitem); 896 | } 897 | } 898 | } 899 | 900 | #endregion Private Methods 901 | } 902 | } 903 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.VisualStudio.Setup.Properties")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | // Setting ComVisible to false makes the types in this assembly not visible 15 | // to COM components. If you need to access a type in this assembly from 16 | // COM, set the ComVisible attribute to true on that type. 17 | [assembly: ComVisible(false)] 18 | 19 | // The following GUID is for the ID of the typelib if this project is exposed to COM 20 | [assembly: Guid("a1d9e507-68cb-4e12-8ee3-a52d9921ee3b")] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | 30 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/SystemSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace Microsoft.VS.ConfigurationManager 5 | { 6 | /// 7 | /// Getting version information and architecture information for use with UninstallAction 8 | /// class. This will define which items are applicable. 9 | /// 10 | /// Source: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832(v=vs.85).aspx 11 | /// Operating system Version number 12 | /// Windows 10 10.0* 13 | /// Windows Server Technical Preview 10.0* 14 | /// Windows 8.1 6.3* 15 | /// Windows Server 2012 R2 6.3* 16 | /// Windows 8 6.2 17 | /// Windows Server 2012 6.2 18 | /// Windows 7 6.1 19 | /// Windows Server 2008 R2 6.1 20 | /// Windows Server 2008 6.0 21 | /// Windows Vista 6.0 22 | /// Windows Server 2003 R2 5.2 23 | /// Windows Server 2003 5.2 24 | /// Windows XP 64-Bit Edition 5.2 25 | /// Windows XP 5.1 26 | /// Windows 2000 5.0 27 | /// 28 | public static class SystemSettings 29 | { 30 | static private string _version = string.Empty; 31 | /// 32 | /// Returns true for 64 bit system and false for x86. 33 | /// 34 | /// 35 | static public bool Is64() 36 | { 37 | return IntPtr.Size == 8 ? true : false; 38 | } 39 | /// 40 | /// Returns Major.Minor version as a string back to the caller. 41 | /// 42 | /// 43 | static public string Version() 44 | { 45 | if (string.IsNullOrEmpty(_version)) 46 | { 47 | var _VersionMajor = Environment.OSVersion.Version.Major.ToString(CultureInfo.InvariantCulture); 48 | var _VersionMinor = Environment.OSVersion.Version.Minor.ToString(CultureInfo.InvariantCulture); 49 | _version = _VersionMajor + "." + _VersionMinor; 50 | } 51 | return _version; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/UninstallAction.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VS.ConfigurationManager.Support; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | 7 | namespace Microsoft.VS.ConfigurationManager 8 | { 9 | /// 10 | /// This class is being created to handle any post uninstall actions that are required. In the 11 | /// case of Dev14, this will handle the /force issues found. If multiple post uninstall 12 | /// actions are required, a list can be used to establish dependencies. 13 | /// 14 | public class UninstallAction 15 | { 16 | /// 17 | /// Constructor to evaluate all values to default values. 18 | /// 19 | public UninstallAction() 20 | { 21 | Template = TemplateType.Unset; 22 | Architectures = new List(); 23 | ProductCode = null; 24 | WixObject = WixObjectType.Unset; 25 | OS = new List(); 26 | } 27 | /// 28 | /// Constructor to handle the creation of an uninstall action with all information passed in. 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | public UninstallAction(ICollection os, TemplateType template, ICollection arch, string productcode, WixObjectType wixobj) 36 | { 37 | Template = template; 38 | Architectures = arch; 39 | ProductCode = productcode; 40 | WixObject = wixobj; 41 | OS = os; 42 | } 43 | 44 | /// 45 | /// Defined to use either bundle or MSI exec installer 46 | /// 47 | public enum WixObjectType { 48 | /// 49 | /// Default value for Enum 50 | /// 51 | Unset, 52 | /// 53 | /// Represents a bundle for this step 54 | /// 55 | Bundle, 56 | /// 57 | /// Represents an MSI for this step 58 | /// 59 | MSI, 60 | /// 61 | /// Represents an MSU for this step 62 | /// 63 | MSU 64 | } 65 | 66 | /// 67 | /// Identify when this step should run; before or after the uninstall process 68 | /// 69 | public enum TemplateType { 70 | /// 71 | /// Default value for Enum 72 | /// 73 | Unset, 74 | /// 75 | /// Run this uninstall action before the main uninstall process 76 | /// 77 | Pre, 78 | /// 79 | /// Run this uninstall action after the main uninstall process 80 | /// 81 | Post 82 | } 83 | 84 | /// 85 | /// What architectures is this uninstall action valid for 86 | /// 87 | public enum Arch { 88 | /// 89 | /// Default value for Enum 90 | /// 91 | Unset, 92 | /// 93 | /// Only valid on x86 configurations 94 | /// 95 | x86, 96 | /// 97 | /// Only valid on 64-bit configurations 98 | /// 99 | x64 100 | } 101 | 102 | private string _productcode; 103 | /// 104 | /// Maps to the installed product code of the WixObjectType 105 | /// 106 | public string ProductCode { 107 | get 108 | { 109 | return _productcode; 110 | } 111 | set 112 | { 113 | _productcode = value == null ? String.Empty : value; 114 | } 115 | } 116 | 117 | /// 118 | /// Is this a bundle or an MSI 119 | /// 120 | public WixObjectType WixObject { get; set; } 121 | 122 | /// 123 | /// Does this run before or after the normal uninstall process 124 | /// 125 | public TemplateType Template { get; set; } 126 | 127 | /// 128 | /// What architectures is this valid on? 129 | /// 130 | public ICollection Architectures { get; set; } 131 | 132 | /// 133 | /// What OS version is this valid on? 134 | /// 135 | public ICollection OS { get; set; } 136 | 137 | /// 138 | /// Create an uninstall action with the given parameters 139 | /// 140 | /// 141 | /// 142 | /// 143 | /// 144 | /// 145 | /// 146 | public static UninstallAction CreateUninstallAction(ICollection archs, ICollection oses, string productcode, UninstallAction.TemplateType template, UninstallAction.WixObjectType objecttype) 147 | { 148 | var ua = new UninstallAction(); 149 | foreach (ArchitectureConfiguration arch in archs) { ua.Architectures.Add(arch); } 150 | foreach (OperatingSystemConfiguration os in oses) { ua.OS.Add(os); } 151 | ua.ProductCode = productcode; 152 | ua.Template = template; 153 | ua.WixObject = objecttype; 154 | 155 | return ua; 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/VS.ConfigurationManager.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | {CACCC6E1-FCB8-4DBE-9159-0F8EAA69D27A} 7 | v3.5 8 | 9 | 10 | 11 | 12 | 13 | Library 14 | 15 | 16 | 17 | 18 | 19 | TRACE;DEBUG;DOTNETFRAMEWORK35; 20 | ManagedMinimumRules.ruleset 21 | false 22 | false 23 | 24 | 25 | {CACCC6E1-FCB8-4DBE-9159-0F8EAA69D27A} 26 | v4.0 27 | 28 | 29 | 30 | TRACE;DOTNETFRAMEWORK35; 31 | 32 | 33 | Setup.VS.ConfigurationManager 34 | 35 | 36 | 37 | False 38 | $(SolutionDir)lib\wix37\Microsoft.Deployment.WindowsInstaller.dll 39 | 40 | 41 | False 42 | $(SolutionDir)lib\wix37\Microsoft.Deployment.WindowsInstaller.Package.dll 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Microsoft 68 | 69 | 70 | 71 | 72 | {13c73873-a5ed-42de-97f0-a3b2d7a1d76f} 73 | VS.ConfigurationManager.Support 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/VS.ConfigurationManager/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/PreBuild.ps1: -------------------------------------------------------------------------------- 1 | # Set up solution paths. 2 | $ToolsDir = split-path -parent $MyInvocation.MyCommand.Path 3 | $SolutionDir = split-path -parent $ToolsDir 4 | 5 | # Restore NuGet packages prior to build. 6 | # This ensures ItemDefinitionGroups for native projects are read at the appropriate time. 7 | $NuGetCommand = join-path $SolutionDir '.nuget\nuget.exe' 8 | &$NuGetCommand restore 9 | -------------------------------------------------------------------------------- /tools/Publish.proj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Release 5 | $(PublishDir)\ 6 | 7 | 8 | 9 | 10 | 11 | 12 | Configuration=$(Configuration);Platform=$(Platform) 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | CheckRequirements; 24 | Build; 25 | $(PublishDependsOn); 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | --------------------------------------------------------------------------------