├── .gitignore ├── LICENSE ├── README.md ├── Test ├── Program.cs ├── ProjectExtensions.cs ├── SpecsBuilder.csproj ├── Test.csproj └── testproj.csproj ├── VsTools.Projects ├── Attribute.cs ├── Compile.cs ├── Content.cs ├── Element.cs ├── EmbeddedResource.cs ├── Folder.cs ├── IDictionaryExtensions.cs ├── Import.cs ├── Item.cs ├── ItemGroup.cs ├── MetadataCollection.cs ├── None.cs ├── PackageReference.cs ├── Page.cs ├── Project.cs ├── ProjectChildElement.cs ├── ProjectElement.cs ├── ProjectExtensions.cs ├── ProjectReference.cs ├── ProjectRoot.cs ├── Property.cs ├── PropertyGroup.cs ├── Reference.cs ├── Reflection.cs ├── Target.cs ├── TypeScriptCompile.cs ├── VsTools.Projects.csproj └── VsTools.Projects.nuspec └── VsTools.sln /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #Ignore thumbnails created by Windows 3 | Thumbs.db 4 | #Ignore files built by Visual Studio 5 | *.obj 6 | *.exe 7 | *.pdb 8 | *.user 9 | *.aps 10 | *.pch 11 | *.vspscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.cache 20 | *.ilk 21 | *.log 22 | [Bb]in 23 | [Dd]ebug*/ 24 | *.lib 25 | *.sbr 26 | obj/ 27 | [Rr]elease*/ 28 | _ReSharper*/ 29 | [Tt]est[Rr]esult* 30 | .vs/ 31 | #Nuget packages folder 32 | packages/ 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 David Khristepher Santos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | **VsTools.Projects** is a set of wrapper classes around XDocument for reading and modifying the contents of Visual Studio C# Project (.csproj) files. 4 | 5 | It grew out of a need to programatically insert generated source files into a project. 6 | 7 | VsTools.Projects only abstracts the most common elements, as the format is completely extensible and can contain custom elements specific to a third-party assembly. 8 | 9 | The API and documentation are currently a work in progress, so expect some changes as the project progresses. 10 | 11 | # NuGet 12 | 13 | ``` 14 | PS> Install-Package VsTools.Projects 15 | ``` 16 | 17 | # Usage 18 | 19 | There are two types of .csproj format. The pre-2017 format was the format used by older .NET Framework projects and has the following root element: 20 | 21 | ```xml 22 | 23 | ``` 24 | 25 | The 2017 format is the format introduced by Visual Studio 2017 for newer .NET Framework projects and .NET Core and has the following root element: 26 | 27 | ```xml 28 | 29 | ``` 30 | 31 | The newer format takes advantage of a lot of default values, and by assuming that all files in the path and sub-paths are part of the project reduces the size of the project file signigicantly. 32 | 33 | VsTools.Projects can read and modify both, but will not validate the contents based on the expected format. It is up to you to make sure you are using the correct elements and attributes. 34 | 35 | Make sure you understand the schema or element hierarchy. 36 | 37 | ## Loading an existing Project 38 | 39 | To load an existing project, use `Project.Load`. The property `Is2017Project` will be `true` if the root element has the attribute `Sdk`. 40 | 41 | ```csharp 42 | var project = new Project.Load("path\\to\\project.csproj"); 43 | 44 | var isNewVersion = project.Is2017Project; 45 | ``` 46 | 47 | ## Adding a file for compilation to a Project 48 | 49 | To add a `.cs` file to a `.csproj`, you need to add a `Compile` element to a new or existing `ItemGroup` element. 50 | 51 | ```csharp 52 | var project = Project.Load("path\\to\\project.csproj"); 53 | 54 | var itemGroup = new ItemGroup(); 55 | 56 | var newFile = new Compile("path\\to\\file.cs"); 57 | 58 | itemGroup.AddContent(newfile); 59 | 60 | project.Add(itemGroup); 61 | 62 | project.Save(); 63 | ``` 64 | 65 | This will insert the following xml as the last child of the `Project` element. 66 | 67 | ```xml 68 | 69 | 70 | 71 | ``` 72 | 73 | ## PropertyGroup 74 | 75 | The `PropertyGroup` element stores project properties such as `PlatformTarget`, `OutputType`, `AssemblyName`, `TargetFrameworkVersion` to name a few, as well as other custom properties. 76 | 77 | ### Accessing PropertyGroup Properties 78 | 79 | PropertyGroup element propeties can be accessed through the `PropertyGroup.Properties` collection. Properties are of type `StringPropertyType` which allow you to set a `Condition`. 80 | 81 | ```csharp 82 | var debug = project.PropertyGroups.First(x => x.Condition == " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "); 83 | 84 | Console.WriteLine(debug.Properties["DebugSymbols"].Value); 85 | ``` 86 | 87 | ### Getting and Setting PropertyGroup Properties 88 | 89 | The `PropertyGroup` class provides convenience methods `GetProperty` and `SetProperty` which avoids having to go through a `StringPropertyType`. 90 | 91 | `GetProperty` returns the string value or `null` if the property does not exist. 92 | 93 | ```csharp 94 | propertyGroup.GetProperty("Configuration");\ 95 | ``` 96 | 97 | `SetProperty` is overloaded with a parameter to set a `Condition` attribute on the property. Setting a property value to `null` will remove the property element. 98 | 99 | ```csharp 100 | // Set the property 'Configuration' to 'Debug' with a condition 101 | propertyGroup.SetProperty("Configuration", "Debug", " '$(Configuration)' == '' "); 102 | // Set the property 'OutputType' to 'Exe' 103 | propertyGroup.SetProperty("OutputType", "Exe"); 104 | // Remove property 'SccLocalPath' 105 | propertyGroup.SetProperty("SccLocalPath", null); 106 | ``` 107 | 108 | Setting a property can also be done through the `PropertyGroup.Properties` collection. 109 | 110 | ```csharp 111 | debug.Properties["DebugSymbols"] = new Property("Configuration", "Debug") { Condition = " '$(Configuration)' == '' " }; 112 | ``` 113 | 114 | ## ItemGroup and Item 115 | 116 | An `ItemGroup` contains a collection of `Item` objects, which define inputs into the build system. 117 | 118 | Common items are `Reference`, `Compile`, `Resource`, and `None`, which tell the build system how to handle the specified item. 119 | 120 | Items should inherit from the `Item` class. Items have a required `Include` attribute, and 0 or more Item Metadata that are usually encoded as child elements, for example, a `Reference` can have a `HintPath` 121 | 122 | ```csharp 123 | var references = new ItemGroup(); 124 | 125 | var reference = new Reference("Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"); 126 | 127 | reference.HintPath = "..\\packages\\Newtonsoft.Json.11.0.1\\lib\\net45\\Newtonsoft.Json.dll"; 128 | 129 | reference.Private = "True"; 130 | 131 | references.Add(reference); 132 | 133 | var files = new ItemGroup(); 134 | 135 | var file = new Compile("path\\to\\class.cs"); 136 | 137 | file.DependentUpon = "path\\to\\page.cshtml"; 138 | 139 | files.Add(file); 140 | 141 | project.Add(references); 142 | project.Add(files); 143 | 144 | ``` 145 | 146 | This will generate the following XML. 147 | 148 | ```xml 149 | 150 | 151 | ..\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll 152 | True 153 | 154 | 155 | 156 | 157 | page.cshtml 158 | 159 | 160 | 161 | ``` 162 | 163 | ## Item Metadata 164 | 165 | For convenience, known Metadata for an element are exposed as string properties on the Item implementation, e.g. `Reference` has a `HintPath` property as shown above. 166 | 167 | It is possible to add custom metadata using `AddOrUpdateMetadataValue` on a class inheriting from `Item`, or `SetMetadata` or `AddMetadata` on any class inheriting from `ProjectElement`. 168 | 169 | ## Conditions 170 | 171 | In MSBuild, Conditions allow for conditional compilation based on defined project properties and other user defined conditions. Conditions are implemented as a `Condition` attribute on most elememts. Setting a condition on an `ItemGroup` for example will cause items under that item group to be added only if the condition is met. 172 | 173 | All classes that inherit from `ProjectElement` have a `Condition` property that sets the underlying element's `Condition` attribute. 174 | 175 | The following will cause the itemGroup's items to be included during compilation if the Debug configuration is specified. 176 | 177 | ```csharp 178 | itemGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "; 179 | ``` 180 | 181 | In order to set a `Condition` attribute on an item Metadata, ensure that the Metadata has been created by setting it through the property, then access the Metadata as a `Property` through the items `Metadata` collection property 182 | 183 | ```csharp 184 | reference.Metadata["Project"].Condition = " '${CustomProperty}' == 'CustomValue' "; 185 | ``` 186 | -------------------------------------------------------------------------------- /Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using VsTools.Projects; 4 | 5 | namespace Test 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | Add(); 12 | } 13 | 14 | public static void Create() 15 | { 16 | var testproject = ProjectExtensions.CreateDefaultConsole("netcoreapp3.1"); 17 | 18 | var itemGroup1 = new ItemGroup(); 19 | 20 | var newFile1 = new Compile("testfile.cs"); 21 | 22 | itemGroup1.Add(newFile1); 23 | 24 | testproject.Add(itemGroup1); 25 | 26 | testproject.SaveAs("testconsole.csproj"); 27 | } 28 | 29 | public static void Add() 30 | { 31 | var project = Project.Load("testproj.csproj"); 32 | 33 | var references = new ItemGroup(); 34 | 35 | var reference = new Reference("Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"); 36 | 37 | reference.HintPath = "..\\packages\\Newtonsoft.Json.11.0.1\\lib\\net45\\Newtonsoft.Json.dll"; 38 | 39 | reference.Private = "True"; 40 | 41 | references.Add(reference); 42 | 43 | var files = new ItemGroup(); 44 | 45 | var file = new Compile("path\\to\\class.cs"); 46 | 47 | file.DependentUpon = "path\\to\\page.cshtml"; 48 | 49 | files.Add(file); 50 | 51 | project.Add(references); 52 | project.Add(files); 53 | 54 | project.Save(); 55 | } 56 | 57 | 58 | public static void AddBeforeLastImport() 59 | { 60 | var project = Project.Load("testproj.csproj"); 61 | 62 | var lastImport = project.Imports.Last(); 63 | 64 | var itemGroup = new ItemGroup(); 65 | 66 | var newFile = new Compile("testfile.cs"); 67 | 68 | itemGroup.Add(newFile); 69 | 70 | lastImport.AddBeforeSelf(itemGroup); 71 | 72 | project.Save(); 73 | } 74 | 75 | public static void AddAfterFirstItemGroup() 76 | { 77 | var project = Project.Load("testproj.csproj"); 78 | 79 | var itemGroup = new ItemGroup(); 80 | 81 | var newFile = new Compile("testfile.cs"); 82 | 83 | itemGroup.Add(newFile); 84 | 85 | var firstItemGroup = project.ItemGroups.First(); 86 | 87 | firstItemGroup.AddAfterSelf(itemGroup); 88 | 89 | var referenceItemGroup = new ItemGroup(); 90 | 91 | var guid = Guid.NewGuid(); 92 | 93 | var reference = new ProjectReference("../classlibrary/classlibrary.csproj") 94 | { 95 | Project = $"{{{guid}}}", 96 | Name = "Some.Namespace", 97 | }; 98 | 99 | // Add a Condition attribute to an item Metadata 100 | reference.Metadata["Project"].Condition = " '${CustomProperty}' == 'CustomValue' "; 101 | 102 | referenceItemGroup.Add(reference); 103 | 104 | itemGroup.AddAfterSelf(referenceItemGroup); 105 | 106 | project.Save(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Test/ProjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using VsTools.Projects; 3 | using Project = VsTools.Projects.Project; 4 | 5 | namespace Test 6 | { 7 | 8 | public static class ProjectExtensions 9 | { 10 | public static Project CreateDefaultConsole(Guid projectGuid, string rootNameSpace, string assemblyName, string targetFrameworkVersion) 11 | { 12 | var project = Project.CreatePreVS2017Project(); 13 | 14 | var import = new Import(@"$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props") 15 | { 16 | Condition = @"Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" 17 | }; 18 | project.Add(import); 19 | 20 | var propertyGroup = new PropertyGroup(); 21 | propertyGroup.SetProperty("Configuration", "Debug", " '$(Configuration)' == '' "); 22 | propertyGroup.SetProperty("Platform", "AnyCPU", " '$(Platform)' == '' "); 23 | propertyGroup.SetProperty("ProjectGuid", $"{{{projectGuid.ToString().ToUpper()}}}"); 24 | propertyGroup.SetProperty("OutputType", "Exe"); 25 | propertyGroup.SetProperty("RootNamespace", rootNameSpace); 26 | propertyGroup.SetProperty("AssemblyName", assemblyName); 27 | propertyGroup.SetProperty("TargetFrameworkVersion", targetFrameworkVersion); 28 | propertyGroup.SetProperty("FileAlignment", "512"); 29 | propertyGroup.SetProperty("AutoGenerateBindingRedirects", "true"); 30 | propertyGroup.SetProperty("Deterministic", "true"); 31 | project.Add(propertyGroup); 32 | 33 | var debugPropertyGroup = new PropertyGroup { Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " }; 34 | debugPropertyGroup.SetProperty("PlatformTarget", "AnyCPU"); 35 | debugPropertyGroup.SetProperty("DebugSymbols", "true"); 36 | debugPropertyGroup.SetProperty("DebugType", "true"); 37 | debugPropertyGroup.SetProperty("DebugSymbols", "full"); 38 | debugPropertyGroup.SetProperty("Optimize", "false"); 39 | debugPropertyGroup.SetProperty("OutputPath", @"bin\Debug\"); 40 | debugPropertyGroup.SetProperty("DefineConstants", "DEBUG;TRACE"); 41 | debugPropertyGroup.SetProperty("ErrorReport", "prompt"); 42 | debugPropertyGroup.SetProperty("WarningLevel", "4"); 43 | project.Add(debugPropertyGroup); 44 | 45 | var releasePropertyGroup = new PropertyGroup { Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " }; 46 | releasePropertyGroup.SetProperty("PlatformTarget", "AnyCPU"); 47 | releasePropertyGroup.SetProperty("DebugType", "pdbonly"); 48 | releasePropertyGroup.SetProperty("Optimize", "true"); 49 | releasePropertyGroup.SetProperty("OutputPath", @"bin\Release\"); 50 | releasePropertyGroup.SetProperty("DefineConstants", "TRACE"); 51 | releasePropertyGroup.SetProperty("ErrorReport", "prompt"); 52 | releasePropertyGroup.SetProperty("WarningLevel", "4"); 53 | project.Add(releasePropertyGroup); 54 | 55 | var referenceItemGroup = new ItemGroup(); 56 | referenceItemGroup.Add(new Reference("System")); 57 | referenceItemGroup.Add(new Reference("System.Core")); 58 | referenceItemGroup.Add(new Reference("System.Xml.Linq")); 59 | referenceItemGroup.Add(new Reference("System.Data.DataSetExtensions")); 60 | referenceItemGroup.Add(new Reference("Microsoft.CSharp")); 61 | referenceItemGroup.Add(new Reference("System.Data")); 62 | referenceItemGroup.Add(new Reference("System.Net.Http")); 63 | referenceItemGroup.Add(new Reference("System.Xml")); 64 | project.Add(referenceItemGroup); 65 | 66 | var targetsImport = new Import(@"$(MSBuildToolsPath)\Microsoft.CSharp.targets"); 67 | project.Add(targetsImport); 68 | 69 | return project; 70 | } 71 | 72 | public static Project CreateDefaultConsole(string targetFramework) 73 | { 74 | var project = Project.CreateVS2017Project(); 75 | 76 | var propertyGroup = new PropertyGroup(); 77 | propertyGroup.SetProperty("OutputType", "Exe"); 78 | propertyGroup.SetProperty("TargetFramework", targetFramework); 79 | project.Add(propertyGroup); 80 | 81 | var itemGroup = new ItemGroup(); 82 | var appSettings = new None() { Update = "appsettings.json" }; 83 | appSettings.SetMetadata("CopyToOutputDirectory", "PreserveNewest"); 84 | itemGroup.Add(appSettings); 85 | project.Add(itemGroup); 86 | 87 | return project; 88 | } 89 | 90 | } 91 | } -------------------------------------------------------------------------------- /Test/SpecsBuilder.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F97EB44F-A4CD-41B7-8281-474D96D19563} 8 | WinExe 9 | Properties 10 | SpecsBuilder 11 | SpecsBuilder 12 | v4.5 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | SpecsBuilder.ico 38 | 39 | 40 | 41 | ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 4.0 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | MSBuild:Compile 66 | Designer 67 | 68 | 69 | 70 | BadgerReportsWindow.xaml 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | ReportBuilderWindow.xaml 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | AllOptionControl.xaml 108 | 109 | 110 | GroupTreeControl.xaml 111 | 112 | 113 | HierarchyOptionControl.xaml 114 | 115 | 116 | SelectOptionControl.xaml 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | CrossGroupDialog.xaml 131 | 132 | 133 | 134 | 135 | RawDataViewWindow.xaml 136 | 137 | 138 | 139 | True 140 | True 141 | MainWindowResources.resx 142 | 143 | 144 | ProgressWindow.xaml 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | VolumeBuilderWindow.xaml 166 | 167 | 168 | FindReplaceWindow.xaml 169 | 170 | 171 | SearchResults.xaml 172 | 173 | 174 | RawView.xaml 175 | 176 | 177 | 178 | Designer 179 | MSBuild:Compile 180 | 181 | 182 | Designer 183 | MSBuild:Compile 184 | 185 | 186 | Designer 187 | MSBuild:Compile 188 | 189 | 190 | Designer 191 | MSBuild:Compile 192 | 193 | 194 | Designer 195 | MSBuild:Compile 196 | 197 | 198 | Designer 199 | MSBuild:Compile 200 | 201 | 202 | Designer 203 | MSBuild:Compile 204 | 205 | 206 | Designer 207 | MSBuild:Compile 208 | 209 | 210 | Designer 211 | MSBuild:Compile 212 | 213 | 214 | Designer 215 | MSBuild:Compile 216 | 217 | 218 | MSBuild:Compile 219 | Designer 220 | 221 | 222 | App.xaml 223 | Code 224 | 225 | 226 | MainWindow.xaml 227 | Code 228 | 229 | 230 | Designer 231 | MSBuild:Compile 232 | 233 | 234 | Designer 235 | MSBuild:Compile 236 | 237 | 238 | Designer 239 | MSBuild:Compile 240 | 241 | 242 | 243 | 244 | Code 245 | 246 | 247 | True 248 | True 249 | Resources.resx 250 | 251 | 252 | True 253 | Settings.settings 254 | True 255 | 256 | 257 | PublicResXFileCodeGenerator 258 | MainWindowResources.Designer.cs 259 | 260 | 261 | ResXFileCodeGenerator 262 | Resources.Designer.cs 263 | 264 | 265 | 266 | SettingsSingleFileGenerator 267 | Settings.Designer.cs 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | {47f4fc98-ed29-488b-ac9c-b60dc25e10dd} 277 | ULTools 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 373 | -------------------------------------------------------------------------------- /Test/Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Exe 6 | net452;net462;net48;netcoreapp3.1 7 | 8 | 9 | 10 | 11 | Always 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Test/testproj.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | {E90B1572-820F-4581-9351-DB36703E6846} 6 | Properties 7 | Test 8 | Test 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /VsTools.Projects/Attribute.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | /// 6 | /// A wrapper around 7 | /// 8 | public class Attribute 9 | { 10 | private readonly XAttribute _attribute; 11 | 12 | public string Value 13 | { 14 | get { return _attribute.Value; } 15 | set { _attribute.Value = value; } 16 | } 17 | 18 | public Attribute(XAttribute attribute) 19 | { 20 | this._attribute = attribute; 21 | } 22 | 23 | public Attribute(string name, string value) 24 | { 25 | this._attribute = new XAttribute(XName.Get(name), value); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /VsTools.Projects/Compile.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class Compile : Item 6 | { 7 | public string DependentUpon 8 | { 9 | get => GetMetadataValue(); 10 | set => AddOrUpdateMetadataValue(value); 11 | } 12 | 13 | public string SubType 14 | { 15 | get => GetMetadataValue(); 16 | set => AddOrUpdateMetadataValue(value); 17 | } 18 | 19 | public string CopyToOutputDirectory 20 | { 21 | get => GetMetadataValue(); 22 | set => AddOrUpdateMetadataValue(value); 23 | } 24 | 25 | public Compile() 26 | { 27 | 28 | } 29 | 30 | public Compile(XNode node) : base(node) 31 | { 32 | 33 | } 34 | 35 | public Compile(string include) : base(include) 36 | { 37 | 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /VsTools.Projects/Content.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class Content : Item 6 | { 7 | public string CopyToOutputDirectory 8 | { 9 | get => GetMetadataValue(); 10 | set => AddOrUpdateMetadataValue(value); 11 | } 12 | 13 | public string DependentUpon 14 | { 15 | get => GetMetadataValue(); 16 | set => AddOrUpdateMetadataValue(value); 17 | } 18 | 19 | public Content(XNode node) : base(node) 20 | { 21 | 22 | } 23 | 24 | public Content(string include) : base(include) 25 | { 26 | 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /VsTools.Projects/Element.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class Element : ProjectElement 6 | { 7 | private string _value; 8 | private readonly string _elementName; 9 | public override int Depth { get; } 10 | 11 | public override string ElementName => _elementName; 12 | 13 | public string Value 14 | { 15 | get { return _value; } 16 | set 17 | { 18 | Element.Value = value; 19 | _value = value; 20 | } 21 | } 22 | 23 | public Element(string name, string value, int depth) 24 | { 25 | _elementName = name; 26 | Value = value; 27 | Depth = depth; 28 | } 29 | 30 | public Element(XNode node, int depth) : base(node) 31 | { 32 | _elementName = Element.Name.LocalName; 33 | _value = Element.Value; 34 | Depth = depth; 35 | } 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /VsTools.Projects/EmbeddedResource.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class EmbeddedResource : Item 6 | { 7 | public string LogicalName 8 | { 9 | get => GetMetadataValue(); 10 | set => AddOrUpdateMetadataValue(value); 11 | } 12 | 13 | public string DependentUpon 14 | { 15 | get => GetMetadataValue(); 16 | set => AddOrUpdateMetadataValue(value); 17 | } 18 | 19 | public string CopyToOutputDirectory 20 | { 21 | get => GetMetadataValue(); 22 | set => AddOrUpdateMetadataValue(value); 23 | } 24 | 25 | public EmbeddedResource() 26 | { 27 | } 28 | 29 | public EmbeddedResource(XNode node) : base(node) 30 | { 31 | } 32 | 33 | public EmbeddedResource(string include) : base(include) 34 | { 35 | } 36 | 37 | //public Property GetMetadataElement([CallerMemberName] string name = null) 38 | //{ 39 | // var element = Element.Elements(name).FirstOrDefault(); 40 | // return element != null ? new Property(element) : null; 41 | //} 42 | 43 | //public void SetMetadataElement(Property element, [CallerMemberName] string name = null) 44 | //{ 45 | // var existing = Element.Elements(name).FirstOrDefault(); 46 | // existing?.Remove(); 47 | // Element.Add(element.Node); 48 | //} 49 | 50 | } 51 | } -------------------------------------------------------------------------------- /VsTools.Projects/Folder.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class Folder : Item 6 | { 7 | public Folder() 8 | { 9 | 10 | } 11 | 12 | public Folder(XNode node) : base(node) 13 | { 14 | 15 | } 16 | 17 | public Folder(string include) : base(include) 18 | { 19 | 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /VsTools.Projects/IDictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml.Linq; 3 | 4 | namespace VsTools.Projects 5 | { 6 | public static class IDictionaryExtensions 7 | { 8 | /// 9 | /// Returns the text value of the matching element in the dicionary, or null if the element name is not found 10 | /// 11 | /// 12 | /// 13 | /// 14 | public static string TryGetXElementValue(this IDictionary elementDictionary, string name) 15 | { 16 | return elementDictionary.TryGetValue(name, out XElement value) ? value.Value : null; 17 | } 18 | 19 | /// 20 | /// Returns the text value of the matching element in the dicionary, throws an exception otherwise 21 | /// 22 | /// 23 | /// 24 | /// 25 | public static string GetXElementValue(this IDictionary elementDictionary, string name) 26 | { 27 | return elementDictionary[name].Value; 28 | } 29 | 30 | } 31 | } -------------------------------------------------------------------------------- /VsTools.Projects/Import.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class Import : ProjectChildElement 6 | { 7 | public string Project 8 | { 9 | get => GetAttributeValue(); 10 | set => AddOrUpdateAttribute(value); 11 | } 12 | 13 | public Import(string project) 14 | { 15 | Project = project; 16 | } 17 | 18 | public Import(XNode node): base(node) 19 | { 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /VsTools.Projects/Item.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Xml.Linq; 3 | 4 | namespace VsTools.Projects 5 | { 6 | /// 7 | /// Represents an untyped ItemGroup child element 8 | /// 9 | public class Item : ProjectElement 10 | { 11 | private MetadataCollection _metadata; 12 | 13 | public override int Depth => 2; 14 | 15 | public string Include 16 | { 17 | get => GetAttributeValue(); 18 | set => AddOrUpdateAttribute(value); 19 | } 20 | 21 | public string Exclude 22 | { 23 | get => GetAttributeValue(); 24 | set => AddOrUpdateAttribute(value); 25 | } 26 | 27 | protected Item() 28 | { 29 | _metadata = new MetadataCollection(this); 30 | } 31 | 32 | protected Item(XNode node) : base(node) 33 | { 34 | _metadata = new MetadataCollection(this); 35 | } 36 | 37 | protected Item(string include) 38 | { 39 | _metadata = new MetadataCollection(this); 40 | Include = include; 41 | } 42 | 43 | public Item(string name, string include) : base(name) 44 | { 45 | _metadata = new MetadataCollection(this); 46 | Include = include; 47 | } 48 | 49 | //https://docs.microsoft.com/en-us/visualstudio/msbuild/item-element-msbuild?view=vs-2019#specify-metadata-as-attributes 50 | 51 | public string GetMetadataValue([CallerMemberName] string name = null) 52 | { 53 | var element = GetElement(name); 54 | if (element != null) 55 | { 56 | return element.Value; 57 | } 58 | return GetAttributeValue(name); 59 | } 60 | 61 | public void AddOrUpdateMetadataValue(string value, [CallerMemberName] string name = null) 62 | { 63 | //AddOrUpdateAttribute(value, name); 64 | AddOrUpdateElement(name, value); 65 | } 66 | 67 | public MetadataCollection Metadata => _metadata; 68 | 69 | 70 | } 71 | 72 | 73 | } -------------------------------------------------------------------------------- /VsTools.Projects/ItemGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Xml.Linq; 5 | 6 | namespace VsTools.Projects 7 | { 8 | public class ItemGroup : ProjectChildElement 9 | { 10 | public ItemGroup() 11 | { 12 | } 13 | 14 | public ItemGroup(XNode node) : base(node) 15 | { 16 | 17 | } 18 | 19 | public void Add(Item content) 20 | { 21 | AddElement(content.Node); 22 | } 23 | 24 | public IEnumerable Items 25 | { 26 | get 27 | { 28 | // I know creating new objects every time you access the contents is very inefficient 29 | return Element.Elements() 30 | .Select(x => 31 | { 32 | var type = Reflection.GetItemGroupContentTypeFromName(x.Name.LocalName); 33 | return (Item) Activator.CreateInstance(type, new object[] {x}); 34 | }); 35 | } 36 | } 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /VsTools.Projects/MetadataCollection.cs: -------------------------------------------------------------------------------- 1 | namespace VsTools.Projects 2 | { 3 | public class MetadataCollection 4 | { 5 | private readonly ProjectElement _parent; 6 | 7 | public MetadataCollection(ProjectElement parent) 8 | { 9 | _parent = parent; 10 | } 11 | 12 | public Property this[string index] 13 | { 14 | get => _parent.GetMetadata(index); 15 | set => _parent.SetMetadata(value); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /VsTools.Projects/None.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class None : Item 6 | { 7 | public string Update 8 | { 9 | get => GetAttributeValue(); 10 | set => AddOrUpdateAttribute(value); 11 | } 12 | 13 | public string DependentUpon 14 | { 15 | get => GetMetadataValue(); 16 | set => AddOrUpdateMetadataValue(value); 17 | } 18 | 19 | public string CopyToOutputDirectory 20 | { 21 | get => GetMetadataValue(); 22 | set => AddOrUpdateMetadataValue(value); 23 | } 24 | 25 | public None() 26 | { 27 | 28 | } 29 | 30 | public None(XNode node) : base(node) 31 | { 32 | 33 | } 34 | 35 | public None(string include) : base(include) 36 | { 37 | 38 | } 39 | 40 | } 41 | } -------------------------------------------------------------------------------- /VsTools.Projects/PackageReference.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class PackageReference : Item 6 | { 7 | public string Version 8 | { 9 | get => GetMetadataValue(); 10 | set => AddOrUpdateMetadataValue(value); 11 | } 12 | 13 | public PackageReference(XNode node) : base(node) 14 | { 15 | } 16 | 17 | public PackageReference(string include, string version) : base(include) 18 | { 19 | Version = version; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /VsTools.Projects/Page.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class Page : Item 6 | { 7 | public string SubType 8 | { 9 | get => GetMetadataValue(); 10 | set => AddOrUpdateMetadataValue(value); 11 | } 12 | 13 | public string Generator 14 | { 15 | get => GetMetadataValue(); 16 | set => AddOrUpdateMetadataValue(value); 17 | } 18 | 19 | public Page() 20 | { 21 | 22 | } 23 | 24 | public Page(XNode node) : base(node) 25 | { 26 | 27 | } 28 | 29 | public Page(string include) : base(include) 30 | { 31 | 32 | } 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /VsTools.Projects/Project.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Xml; 6 | using System.Xml.Linq; 7 | 8 | namespace VsTools.Projects 9 | { 10 | public class Project 11 | { 12 | internal static XNamespace MsbuildSchema = "http://schemas.microsoft.com/developer/msbuild/2003"; 13 | 14 | private readonly XDocument _source; 15 | private string _sourceFile; 16 | 17 | public bool Is2017Project => _source.Root.Attribute("Sdk") != null; 18 | 19 | /// 20 | /// Instantiates a new Project 21 | /// 22 | private Project(bool is2017Project, string toolsVersion = "15.0") 23 | { 24 | _source = new XDocument(); 25 | var root = new ProjectRoot(is2017Project, toolsVersion); 26 | _source.Add(root.Node); 27 | } 28 | 29 | public static Project CreatePreVS2017Project(string toolsVersion = "15.0") 30 | { 31 | return new Project(false, toolsVersion); 32 | } 33 | 34 | public static Project CreateVS2017Project() 35 | { 36 | return new Project(true); 37 | } 38 | 39 | /// 40 | /// Instantiates a Project from an XML document 41 | /// 42 | /// 43 | public Project(XDocument source) 44 | { 45 | _source = source; 46 | } 47 | 48 | 49 | 50 | /// 51 | /// Loads a .csporj file and returns a new Project instance 52 | /// 53 | /// 54 | /// 55 | public static Project Load(string file) 56 | { 57 | var xml = XDocument.Load(file, LoadOptions.PreserveWhitespace); 58 | if (xml.Root.Name.LocalName != "Project") 59 | { 60 | throw new Exception("Invalid Project file, root element not found!"); 61 | } 62 | 63 | var project = new Project(xml) { _sourceFile = file }; 64 | 65 | return project; 66 | } 67 | 68 | public void Add(ProjectChildElement element) 69 | { 70 | new ProjectRoot(_source.Root).AddChild(element); 71 | } 72 | 73 | public IEnumerable ItemGroups 74 | { 75 | get 76 | { 77 | return _source.Root.Elements().Where(x => x.Name.LocalName == "ItemGroup").Select(x => new ItemGroup(x)); 78 | } 79 | } 80 | 81 | public IEnumerable Imports 82 | { 83 | get 84 | { 85 | return _source.Root.Elements().Where(x => x.Name.LocalName == "Import").Select(x => new Import(x)); 86 | } 87 | } 88 | 89 | public IEnumerable PropertyGroups 90 | { 91 | get 92 | { 93 | return _source.Root.Elements().Where(x => x.Name.LocalName == "PropertyGroup").Select(x => new PropertyGroup(x)); 94 | } 95 | } 96 | 97 | 98 | /// 99 | /// Returns a instance with an Include matching the given string, or null otherwise 100 | /// 101 | /// 102 | /// 103 | /// 104 | public T FindItemGroupContent(string include) where T : Item, new() 105 | { 106 | var node = 107 | _source.DescendantNodes() 108 | .Where(x => x is XElement) 109 | .FirstOrDefault( 110 | x => 111 | ((XElement)x).Name.LocalName == typeof(T).Name && 112 | ((XElement)x).Attributes().First(y => y.Name.LocalName == "Include").Value == include); 113 | 114 | if (node == null) return default(T); 115 | 116 | return (T)Activator.CreateInstance(typeof(T), new object[] { node }); 117 | } 118 | 119 | /// 120 | /// Returns true if an with a matching Include exists in the Project 121 | /// 122 | /// 123 | /// 124 | /// 125 | public bool ItemGroupContentExists(string include) where T : Item 126 | { 127 | return _source.DescendantNodes() 128 | .Where(x => x is XElement) 129 | .FirstOrDefault( 130 | x => 131 | ((XElement)x).Name.LocalName == typeof(T).Name && 132 | ((XElement)x).Attributes().First(y => y.Name.LocalName == "Include").Value == include) != null; 133 | } 134 | 135 | /// 136 | /// Returns a instance with an Include matching the given string, or null otherwise 137 | /// 138 | /// 139 | /// 140 | /// 141 | public bool ProjectReferenceExists(string include) 142 | { 143 | return _source.DescendantNodes() 144 | .Where(x => x is XElement) 145 | .FirstOrDefault( 146 | x => 147 | ((XElement)x).Name.LocalName == "ProjectReference" && 148 | ((XElement)x).Attributes().First(y => y.Name.LocalName == "Include").Value == include) != null; 149 | } 150 | 151 | /// 152 | /// Returns a instance with an Include matching the given string, or null otherwise 153 | /// 154 | /// 155 | /// 156 | /// 157 | public ProjectReference FindProjectReference(string include) 158 | { 159 | var node = 160 | _source.DescendantNodes() 161 | .Where(x => x is XElement) 162 | .FirstOrDefault( 163 | x => 164 | ((XElement)x).Name.LocalName == "ProjectReference" && 165 | ((XElement)x).Attributes().First(y => y.Name.LocalName == "Include").Value == include); 166 | 167 | if (node == null) return null; 168 | 169 | return new ProjectReference(node); 170 | } 171 | 172 | /// 173 | /// Saves the Project 174 | /// 175 | public void Save() 176 | { 177 | if (_sourceFile != null) 178 | { 179 | SaveAs(_sourceFile); 180 | } 181 | else 182 | { 183 | throw new Exception("Project file was created with Load(). Please use Save(file)"); 184 | } 185 | } 186 | 187 | /// 188 | /// Saves the Project as an Xml Document 189 | /// 190 | /// 191 | public void SaveAs(string file) 192 | { 193 | var settings = new XmlWriterSettings 194 | { 195 | Indent = true, 196 | IndentChars = " ", 197 | }; 198 | 199 | if (!string.IsNullOrEmpty(_source.Root.Name.Namespace.NamespaceName)) 200 | { 201 | SetDefaultXmlNamespace(_source.Root, _source.Root.Name.Namespace); 202 | } 203 | 204 | using (XmlWriter writer = XmlWriter.Create(file, settings)) 205 | { 206 | _source.Save(writer); 207 | } 208 | } 209 | 210 | /// 211 | /// Saves the Project to the given Stream 212 | /// 213 | /// 214 | public void Save(Stream stream) 215 | { 216 | var settings = new XmlWriterSettings 217 | { 218 | Indent = true, 219 | IndentChars = " ", 220 | }; 221 | 222 | if (!string.IsNullOrEmpty(_source.Root.Name.Namespace.NamespaceName)) 223 | { 224 | SetDefaultXmlNamespace(_source.Root, _source.Root.Name.Namespace); 225 | } 226 | 227 | using (XmlWriter writer = XmlWriter.Create(stream, settings)) 228 | { 229 | _source.Save(writer); 230 | } 231 | } 232 | 233 | /// 234 | /// Returns the underlying Xml Document 235 | /// 236 | /// 237 | public XDocument ToXml() 238 | { 239 | return _source; 240 | } 241 | 242 | protected void SetDefaultXmlNamespace(XElement xelem, XNamespace xmlns) 243 | { 244 | if (xelem.Name.NamespaceName == string.Empty) 245 | xelem.Name = xmlns + xelem.Name.LocalName; 246 | foreach (var e in xelem.Elements()) 247 | SetDefaultXmlNamespace(e, xmlns); 248 | } 249 | } 250 | } -------------------------------------------------------------------------------- /VsTools.Projects/ProjectChildElement.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public abstract class ProjectChildElement : ProjectElement 6 | { 7 | public override int Depth => 1; 8 | 9 | protected ProjectChildElement() 10 | { 11 | 12 | } 13 | 14 | protected ProjectChildElement(string name) : base(name) 15 | { 16 | 17 | } 18 | protected ProjectChildElement(XNode node) : base(node) 19 | { 20 | 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /VsTools.Projects/ProjectElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using System.Xml; 6 | using System.Xml.Linq; 7 | 8 | namespace VsTools.Projects 9 | { 10 | public abstract class ProjectElement 11 | { 12 | private XNode ClosingElementSpace 13 | { 14 | get 15 | { 16 | if (Element.Nodes().Any()) 17 | { 18 | var lastNode = Element.Nodes().Last(); 19 | if (lastNode.NodeType == XmlNodeType.Text) 20 | { 21 | var lastNodeText = ((XText)lastNode).Value; 22 | if (lastNodeText.Trim('\r', '\n', ' ') == string.Empty) 23 | { 24 | return lastNode; 25 | } 26 | } 27 | } 28 | return null; 29 | } 30 | } 31 | 32 | public abstract int Depth { get; } 33 | public virtual string ElementName => GetType().Name; 34 | 35 | public XNode Node { get; set; } 36 | public XElement Element => (XElement)Node; 37 | 38 | public string Condition 39 | { 40 | get => GetAttributeValue(); 41 | set => AddOrUpdateAttribute(value); 42 | } 43 | 44 | /// 45 | /// Creates an ProjectElement with an element with the specified name 46 | /// 47 | /// 48 | protected ProjectElement(string name) 49 | { 50 | Node = new XElement(name); 51 | } 52 | 53 | protected ProjectElement() 54 | { 55 | Node = new XElement(ElementName); 56 | } 57 | 58 | protected ProjectElement(XName name) 59 | { 60 | Node = new XElement(name); 61 | } 62 | 63 | protected ProjectElement(XNode node) 64 | { 65 | Node = node; 66 | } 67 | 68 | public void AddChild(ProjectElement element) 69 | { 70 | if (ClosingElementSpace == null) 71 | { 72 | var closingElementSpace = new XText("\r\n" + new string(' ', (Depth) * 2)); 73 | Element.Add(closingElementSpace); 74 | } 75 | Element.Add(new XText(new string(' ', 2))); 76 | Element.Add(element.Node); 77 | element.Node.AddAfterSelf(new XText("\r\n" + new string(' ', Depth * 2))); 78 | } 79 | 80 | public void AddChild(Property element) 81 | { 82 | if (ClosingElementSpace == null) 83 | { 84 | var closingElementSpace = new XText("\r\n" + new string(' ', (Depth) * 2)); 85 | Element.Add(closingElementSpace); 86 | } 87 | Element.Add(new XText(new string(' ', 2))); 88 | Element.Add(element.Node); 89 | element.Node.AddAfterSelf(new XText("\r\n" + new string(' ', Depth * 2))); 90 | } 91 | 92 | 93 | public void AddBeforeSelf(ProjectElement element) 94 | { 95 | Node.AddBeforeSelf(element.Node); 96 | element.Node.AddAfterSelf(new XText("\r\n" + new string(' ', element.Depth * 2))); 97 | } 98 | 99 | public void AddAfterSelf(ProjectElement element) 100 | { 101 | Node.AddAfterSelf(element.Node); 102 | element.Node.AddBeforeSelf(new XText("\r\n" + new string(' ', element.Depth * 2))); 103 | } 104 | 105 | public void RemoveAttribute(string name) 106 | { 107 | var attr = Element.Attributes().First(x => x.Name.LocalName == name); 108 | attr.Remove(); 109 | } 110 | 111 | public string GetAttributeValue([CallerMemberName] string name = null) 112 | { 113 | var attr = Element.Attributes().First(x => x.Name.LocalName == name); 114 | return attr.Value; 115 | } 116 | 117 | /// 118 | /// Adds an non-existing attribute, or updates an existing attribute, or removes the attribute if the value is set to null 119 | /// 120 | /// The name of the attribute to add, update or remove 121 | /// The value of the attribute or null to remove the attribute 122 | public void AddOrUpdateAttribute(string value, [CallerMemberName] string name = null) 123 | { 124 | var attr = Element.Attributes().FirstOrDefault(x => x.Name.LocalName == name); 125 | if (attr != null) 126 | { 127 | if (value == null) 128 | { 129 | RemoveAttribute(name); 130 | } 131 | else 132 | { 133 | attr.Value = value; 134 | } 135 | } 136 | else 137 | { 138 | if (value == null) 139 | { 140 | throw new Exception($"Cannot remove non-existant attribute {name}"); 141 | } 142 | attr = new XAttribute(XName.Get(name), value); 143 | Element.Add(attr); 144 | } 145 | } 146 | 147 | public void AddAttribute(string name, string value) 148 | { 149 | var attr = new XAttribute(XName.Get(name), value); 150 | Element.Add(attr); 151 | } 152 | 153 | public void AddOrUpdateElement(string name, string value, string condition) 154 | { 155 | var child = Element.Elements().FirstOrDefault(x => x.Name.LocalName == name); 156 | if (child != null) 157 | { 158 | child.Value = value; 159 | child.SetAttributeValue("Condition", condition); 160 | } 161 | else 162 | { 163 | if (ClosingElementSpace == null) 164 | { 165 | var closingElementSpace = new XText("\r\n" + new string(' ', (Depth) * 2)); 166 | Element.Add(closingElementSpace); 167 | } 168 | child = new XElement(name); 169 | child.Add(value); 170 | child.SetAttributeValue("Condition", condition); 171 | ClosingElementSpace.AddBeforeSelf(child); 172 | child.AddBeforeSelf(new XText("\r\n" + new string(' ', (Depth + 1) * 2))); 173 | } 174 | } 175 | 176 | public XElement GetElement(string name) 177 | { 178 | return Element.Element(name); 179 | } 180 | 181 | public void AddOrUpdateElement(string name, string value) 182 | { 183 | var child = Element.Elements().FirstOrDefault(x => x.Name.LocalName == name); 184 | if (child != null) 185 | { 186 | if (value == null) 187 | { 188 | child.Remove(); 189 | } 190 | else 191 | { 192 | child.Value = value; 193 | } 194 | } 195 | else 196 | { 197 | if (ClosingElementSpace == null) 198 | { 199 | var closingElementSpace = new XText("\r\n" + new string(' ', (Depth) * 2)); 200 | Element.Add(closingElementSpace); 201 | } 202 | child = new XElement(name); 203 | child.Add(value); 204 | ClosingElementSpace.AddBeforeSelf(child); 205 | child.AddBeforeSelf(new XText("\r\n" + new string(' ', (Depth + 1) * 2))); 206 | } 207 | } 208 | 209 | public void AddElement(XNode child) 210 | { 211 | if (ClosingElementSpace == null) 212 | { 213 | var closingElementSpace = new XText("\r\n" + new string(' ', (Depth) * 2)); 214 | Element.Add(closingElementSpace); 215 | } 216 | ClosingElementSpace.AddBeforeSelf(child); 217 | child.AddBeforeSelf(new XText("\r\n" + new string(' ', (Depth + 1) * 2))); 218 | } 219 | 220 | public Attribute GetAttribute(string name) 221 | { 222 | return new Attribute(Element.Attribute(name)); 223 | } 224 | 225 | public Element GetChildElement(string name) 226 | { 227 | return new Element(Element.Element(name), Depth + 1); 228 | } 229 | 230 | /// 231 | /// Returns the elements attributes. Only use this method if you are accessing custom attributes, otherwise use the properties on the current type 232 | /// 233 | /// 234 | public IEnumerable GetAttributes() 235 | { 236 | return Element.Attributes().Select(x => new Attribute(x)); 237 | } 238 | 239 | /// 240 | /// Returns the child elements. Only use this method if you are accessing custom elements, otherwise use the properties on the current type 241 | /// 242 | /// 243 | public IEnumerable GetChildElements() 244 | { 245 | return Element.Elements().Select(x => new Element(x, Depth + 1)); 246 | } 247 | 248 | 249 | 250 | /// 251 | /// Retrieves the xml elements contained within the current element as a dictionary 252 | /// 253 | /// 254 | protected IDictionary GetXElements() 255 | { 256 | return Element.Elements().ToDictionary(x => x.Name.LocalName); 257 | } 258 | 259 | public Property GetMetadata(string elementName) 260 | { 261 | var existing = Element.Elements().FirstOrDefault(x => x.Name.LocalName == elementName); 262 | return existing == null ? null : new Property(existing); 263 | } 264 | 265 | public bool HasMetadata(string elementName) 266 | { 267 | return Element?.Elements(elementName) != null; 268 | } 269 | 270 | public void SetMetadata(Property element) 271 | { 272 | var existing = Element.Elements().FirstOrDefault(x => x.Name.LocalName == element.ElementName); 273 | existing?.Remove(); 274 | AddChild(element); 275 | } 276 | 277 | public void SetMetadata(string elementName, string value) 278 | { 279 | var existing = Element.Elements().FirstOrDefault(x => x.Name.LocalName == elementName); 280 | existing?.Remove(); 281 | AddChild(new Property(elementName, value)); 282 | } 283 | 284 | public void SetMetadata(string elementName, string value, string condition) 285 | { 286 | var existing = Element.Elements().FirstOrDefault(x => x.Name.LocalName == elementName); 287 | existing?.Remove(); 288 | AddChild(new Property(elementName, value) { Condition = condition }); 289 | } 290 | 291 | public void AddMetadata(string elementName, string value) 292 | { 293 | AddChild(new Property(elementName, value)); 294 | } 295 | 296 | public void AddMetadata(string elementName, string value, string condition) 297 | { 298 | AddChild(new Property(elementName, value) { Condition = condition }); 299 | } 300 | 301 | 302 | 303 | } 304 | } -------------------------------------------------------------------------------- /VsTools.Projects/ProjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public static class ProjectExtensions 6 | { 7 | public static Project CreateDefaultConsole(Guid projectGuid, string rootNameSpace, string assemblyName, string targetFrameworkVersion) 8 | { 9 | var project = Project.CreatePreVS2017Project(); 10 | 11 | var import = new Import(@"$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props") 12 | { 13 | Condition = @"Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" 14 | }; 15 | project.Add(import); 16 | 17 | var propertyGroup = new PropertyGroup(); 18 | propertyGroup.SetMetadata("Configuration", "Debug", " '$(Configuration)' == '' "); 19 | propertyGroup.SetMetadata("Platform", "AnyCPU", " '$(Platform)' == '' "); 20 | propertyGroup.SetMetadata("ProjectGuid", $"{{{projectGuid.ToString().ToUpper()}}}"); 21 | propertyGroup.SetMetadata("OutputType", "Exe"); 22 | propertyGroup.SetMetadata("RootNamespace", rootNameSpace); 23 | propertyGroup.SetMetadata("AssemblyName", assemblyName); 24 | propertyGroup.SetMetadata("TargetFrameworkVersion", targetFrameworkVersion); 25 | propertyGroup.SetMetadata("FileAlignment", "512"); 26 | propertyGroup.SetMetadata("AutoGenerateBindingRedirects", "true"); 27 | propertyGroup.SetMetadata("Deterministic", "true"); 28 | project.Add(propertyGroup); 29 | 30 | var debugPropertyGroup = new PropertyGroup { Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " }; 31 | debugPropertyGroup.SetMetadata("PlatformTarget", "AnyCPU"); 32 | debugPropertyGroup.SetMetadata("DebugSymbols", "true"); 33 | debugPropertyGroup.SetMetadata("DebugType", "true"); 34 | debugPropertyGroup.SetMetadata("DebugSymbols", "full"); 35 | debugPropertyGroup.SetMetadata("Optimize", "false"); 36 | debugPropertyGroup.SetMetadata("OutputPath", @"bin\Debug\"); 37 | debugPropertyGroup.SetMetadata("DefineConstants", "DEBUG;TRACE"); 38 | debugPropertyGroup.SetMetadata("ErrorReport", "prompt"); 39 | debugPropertyGroup.SetMetadata("WarningLevel", "4"); 40 | project.Add(debugPropertyGroup); 41 | 42 | var releasePropertyGroup = new PropertyGroup { Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " }; 43 | releasePropertyGroup.SetMetadata("PlatformTarget", "AnyCPU"); 44 | releasePropertyGroup.SetMetadata("DebugType", "pdbonly"); 45 | releasePropertyGroup.SetMetadata("Optimize", "true"); 46 | releasePropertyGroup.SetMetadata("OutputPath", @"bin\Release\"); 47 | releasePropertyGroup.SetMetadata("DefineConstants", "TRACE"); 48 | releasePropertyGroup.SetMetadata("ErrorReport", "prompt"); 49 | releasePropertyGroup.SetMetadata("WarningLevel", "4"); 50 | project.Add(releasePropertyGroup); 51 | 52 | var referenceItemGroup = new ItemGroup(); 53 | referenceItemGroup.Add(new Reference("System")); 54 | referenceItemGroup.Add(new Reference("System.Core")); 55 | referenceItemGroup.Add(new Reference("System.Xml.Linq")); 56 | referenceItemGroup.Add(new Reference("System.Data.DataSetExtensions")); 57 | referenceItemGroup.Add(new Reference("Microsoft.CSharp")); 58 | referenceItemGroup.Add(new Reference("System.Data")); 59 | referenceItemGroup.Add(new Reference("System.Net.Http")); 60 | referenceItemGroup.Add(new Reference("System.Xml")); 61 | project.Add(referenceItemGroup); 62 | 63 | var targetsImport = new Import(@"$(MSBuildToolsPath)\Microsoft.CSharp.targets"); 64 | project.Add(targetsImport); 65 | 66 | return project; 67 | } 68 | 69 | public static Project CreateDefaultConsole(string targetFramework) 70 | { 71 | var project = Project.CreateVS2017Project(); 72 | 73 | var propertyGroup = new PropertyGroup(); 74 | propertyGroup.SetMetadata("OutputType", "Exe"); 75 | propertyGroup.SetMetadata("TargetFramework", targetFramework); 76 | project.Add(propertyGroup); 77 | 78 | var itemGroup = new ItemGroup(); 79 | var appSettings = new None() { Update = "appsettings.json" }; 80 | appSettings.SetMetadata("CopyToOutputDirectory", "PreserveNewest"); 81 | itemGroup.Add(appSettings); 82 | project.Add(itemGroup); 83 | 84 | return project; 85 | } 86 | 87 | } 88 | } -------------------------------------------------------------------------------- /VsTools.Projects/ProjectReference.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class ProjectReference : Item 6 | { 7 | public string Name 8 | { 9 | get => GetMetadataValue(); 10 | set => AddOrUpdateMetadataValue(value); 11 | } 12 | 13 | public string Project 14 | { 15 | get => GetMetadataValue(); 16 | set => AddOrUpdateMetadataValue(value); 17 | } 18 | 19 | public string Package 20 | { 21 | get => GetMetadataValue(); 22 | set => AddOrUpdateMetadataValue(value); 23 | } 24 | 25 | public ProjectReference(XNode node) : base(node) 26 | { 27 | } 28 | 29 | public ProjectReference(string include) : base(include) 30 | { 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /VsTools.Projects/ProjectRoot.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class ProjectRoot : ProjectElement 6 | { 7 | public string Sdk 8 | { 9 | get => GetAttributeValue(); 10 | set => AddOrUpdateAttribute(value); 11 | } 12 | 13 | public string ToolsVersion 14 | { 15 | get => GetAttributeValue(); 16 | set => AddOrUpdateAttribute(value); 17 | } 18 | 19 | public override int Depth => 0; 20 | 21 | public ProjectRoot() 22 | { 23 | 24 | } 25 | 26 | public ProjectRoot(XNode node) : base(node) 27 | { 28 | 29 | } 30 | 31 | public ProjectRoot(bool is2017Project, string toolsVersion = "15.0") : base(CreateProject(is2017Project, toolsVersion)) 32 | { 33 | } 34 | 35 | 36 | private static XElement CreateProject(bool is2017Project, string toolsVersion) 37 | { 38 | if (is2017Project) 39 | { 40 | return CreateVS2017Project(); 41 | } 42 | else 43 | { 44 | return CreatePreVS2017Project(toolsVersion); 45 | } 46 | } 47 | 48 | private static XElement CreatePreVS2017Project(string toolsVersion = "15.0") 49 | { 50 | var projectElement = new XElement(Project.MsbuildSchema + "Project"); 51 | projectElement.SetAttributeValue("ToolsVersion", toolsVersion); 52 | return projectElement; 53 | } 54 | 55 | private static XElement CreateVS2017Project() 56 | { 57 | var projectElement = new XElement("Project"); 58 | projectElement.SetAttributeValue("Sdk", "Microsoft.NET.Sdk"); 59 | return projectElement; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /VsTools.Projects/Property.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.CompilerServices; 4 | using System.Xml.Linq; 5 | 6 | namespace VsTools.Projects 7 | { 8 | public class Property 9 | { 10 | public XNode Node { get; set; } 11 | public XElement Element => (XElement)Node; 12 | public virtual string ElementName => GetType().Name; 13 | 14 | public string Condition 15 | { 16 | get => GetAttributeValue(); 17 | set => AddOrUpdateAttribute(value); 18 | } 19 | 20 | public Property(string propertyName, string value) 21 | { 22 | Node = new XElement(propertyName); 23 | Element.Add(value); 24 | } 25 | 26 | protected Property(string value) 27 | { 28 | Node = new XElement(ElementName); 29 | Element.Add(new XText(value)); 30 | } 31 | 32 | public Property(XNode node) 33 | { 34 | var type = GetType(); 35 | 36 | if (type != typeof(Property) && ((XElement)node).Name.LocalName != ElementName) 37 | { 38 | throw new Exception($"Element does not match expected type {type.Name}"); 39 | } 40 | Node = node; 41 | } 42 | 43 | public string Value 44 | { 45 | get => ((XText)Element.FirstNode)?.Value; 46 | set 47 | { 48 | if ((Element.FirstNode) == null) 49 | { 50 | Element.Add(new XText(value)); 51 | } 52 | else 53 | { 54 | ((XText)Element.FirstNode).Value = value; 55 | } 56 | } 57 | } 58 | 59 | 60 | public void RemoveAttribute(string name) 61 | { 62 | var attr = Element.Attributes().First(x => x.Name.LocalName == name); 63 | attr.Remove(); 64 | } 65 | 66 | public string GetAttributeValue([CallerMemberName] string name = null) 67 | { 68 | var attr = Element.Attributes().First(x => x.Name.LocalName == name); 69 | return attr.Value; 70 | } 71 | 72 | /// 73 | /// Adds an non-existing attribute, or updates an existing attribute, or removes the attribute if the value is set to null 74 | /// 75 | /// The name of the attribute to add, update or remove 76 | /// The value of the attribute or null to remove the attribute 77 | public void AddOrUpdateAttribute(string value, [CallerMemberName] string name = null) 78 | { 79 | var attr = Element.Attributes().FirstOrDefault(x => x.Name.LocalName == name); 80 | if (attr != null) 81 | { 82 | if (value == null) 83 | { 84 | RemoveAttribute(name); 85 | } 86 | else 87 | { 88 | attr.Value = value; 89 | } 90 | } 91 | else 92 | { 93 | if (value == null) 94 | { 95 | throw new Exception($"Cannot remove non-existant attribute {name}"); 96 | } 97 | attr = new XAttribute(XName.Get(name), value); 98 | Element.Add(attr); 99 | } 100 | } 101 | 102 | } 103 | } -------------------------------------------------------------------------------- /VsTools.Projects/PropertyGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Xml.Linq; 3 | 4 | namespace VsTools.Projects 5 | { 6 | public class PropertyGroup : ProjectChildElement 7 | { 8 | private readonly MetadataCollection _propertyCollection ; 9 | 10 | public PropertyGroup() 11 | { 12 | _propertyCollection = new MetadataCollection(this); 13 | } 14 | 15 | public PropertyGroup(XNode node) : base(node) 16 | { 17 | } 18 | 19 | public bool HasProperty(string property) 20 | { 21 | return HasMetadata(property); 22 | } 23 | 24 | public string GetProperty(string property) 25 | { 26 | return GetMetadata(property)?.Value; 27 | } 28 | 29 | public void SetProperty(string property, string value) 30 | { 31 | SetMetadata(property, value); 32 | } 33 | 34 | public void SetProperty(string property, string value, string condition) 35 | { 36 | SetMetadata(property, value, condition); 37 | } 38 | 39 | public MetadataCollection Properties => _propertyCollection; 40 | } 41 | } -------------------------------------------------------------------------------- /VsTools.Projects/Reference.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace VsTools.Projects 4 | { 5 | public class Reference : Item 6 | { 7 | public string HintPath 8 | { 9 | get => GetMetadataValue(); 10 | set => AddOrUpdateMetadataValue(value); 11 | } 12 | 13 | public string Private 14 | { 15 | get => GetMetadataValue(); 16 | set => AddOrUpdateMetadataValue(value); 17 | } 18 | 19 | public string SpecificVersion 20 | { 21 | get => GetMetadataValue(); 22 | set => AddOrUpdateMetadataValue(value); 23 | } 24 | 25 | public Reference(XNode node) : base(node) 26 | { 27 | } 28 | 29 | public Reference(string include) : base(include) 30 | { 31 | } 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /VsTools.Projects/Reflection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace VsTools.Projects 6 | { 7 | public static class Reflection 8 | { 9 | private static readonly Dictionary _typelookup; 10 | 11 | static Reflection() 12 | { 13 | _typelookup = typeof(Item).Assembly.GetTypes().Where(x => typeof(Item).IsAssignableFrom(x)).ToDictionary(x => x.Name); 14 | } 15 | 16 | public static Type GetItemGroupContentTypeFromName(string name) 17 | { 18 | Type type; 19 | if (!_typelookup.TryGetValue(name, out type)) 20 | { 21 | return typeof(Item); 22 | } 23 | return type; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /VsTools.Projects/Target.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Xml.Linq; 3 | 4 | namespace VsTools.Projects 5 | { 6 | public class Target : ProjectElement 7 | { 8 | public override int Depth => 2; 9 | 10 | public string Name 11 | { 12 | get => GetAttributeValue(); 13 | set => AddOrUpdateAttribute(value); 14 | } 15 | 16 | public string BeforeTargets 17 | { 18 | get => GetAttributeValue(); 19 | set => AddOrUpdateAttribute(value); 20 | } 21 | 22 | public string AfterTargets 23 | { 24 | get => GetAttributeValue(); 25 | set => AddOrUpdateAttribute(value); 26 | } 27 | 28 | public Target(XNode node) : base(node) 29 | { 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /VsTools.Projects/TypeScriptCompile.cs: -------------------------------------------------------------------------------- 1 | namespace VsTools.Projects 2 | { 3 | public class TypeScriptCompile : Item 4 | { 5 | public string SubType 6 | { 7 | get => GetMetadataValue(); 8 | set => AddOrUpdateMetadataValue(value); 9 | } 10 | 11 | public string DependentUpon 12 | { 13 | get => GetMetadataValue(); 14 | set => AddOrUpdateMetadataValue(value); 15 | } 16 | 17 | public TypeScriptCompile(string include) : base(include) 18 | { 19 | 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /VsTools.Projects/VsTools.Projects.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net452;net462;net48;netstandard2.0; 5 | true 6 | A library for reading and modifying .csproj files 7 | 1.2.0 8 | ©2020 David Khristepher Santos 9 | RupertAvery 10 | RupertAvery 11 | https://github.com/RupertAvery/VsTools.Projects 12 | https://github.com/RupertAvery/VsTools.Projects 13 | csproj xml automation 14 | Fixes a lot of issues and updates the API, more in line with the actual schema 15 | 16 | -------------------------------------------------------------------------------- /VsTools.Projects/VsTools.Projects.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $id$ 5 | 1.0 6 | VsTools.Projects 7 | David Khristepher Santos 8 | $author$ 9 | https://github.com/RupertAvery/VsTools.Projects 10 | false 11 | A library for reading and modifying CSPROJ files easily 12 | Copyright 2017 13 | csproj xml automation visual-studio tool 14 | 15 | -------------------------------------------------------------------------------- /VsTools.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VsTools.Projects", "VsTools.Projects\VsTools.Projects.csproj", "{D9F304B9-22C9-4436-9B2D-173131DAFA88}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{93FC0CDD-D95F-4FA2-888D-EE1295D9F053}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {D9F304B9-22C9-4436-9B2D-173131DAFA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {D9F304B9-22C9-4436-9B2D-173131DAFA88}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {D9F304B9-22C9-4436-9B2D-173131DAFA88}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {D9F304B9-22C9-4436-9B2D-173131DAFA88}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {93FC0CDD-D95F-4FA2-888D-EE1295D9F053}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {93FC0CDD-D95F-4FA2-888D-EE1295D9F053}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {93FC0CDD-D95F-4FA2-888D-EE1295D9F053}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {93FC0CDD-D95F-4FA2-888D-EE1295D9F053}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4F2CD0FF-91D4-46F3-9924-16B581DA7D51} 30 | EndGlobalSection 31 | EndGlobal 32 | --------------------------------------------------------------------------------