I have a solution with Web Site project. There are three custom Solution configuration: Dev, Test, Prod. However, Web Site project doesn't have .csproj file and there is no way(or I can't find it out) to use configurations for Web Site project.
I'm using MSBuild to build the solution:
MSBuild.exe MySolution.sln /m /p:Platform=x86;TargetFrameworkVersion=v4.6.2;OutputPath=bin;Configuration=Dev /ToolsVersion:14.0
As .sln file doesn't have any information about my Dev configuration for Web Site project, there are only Debug and Release "sections":
Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Web", "Web\", "{1F81A0DB-6FF4-4F89-B988-6D327797C4FD}"
ProjectSection(WebsiteProperties) = preProject
TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.6.2"
ProjectReferences = "{12A625AA-8B5A-4336-AA4A-3630AAB3A27D}|MyLib.dll;"
Debug.AspNetCompiler.VirtualPath = "/localhost_54141"
Debug.AspNetCompiler.PhysicalPath = "Web\"
Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_54141\"
Debug.AspNetCompiler.Updateable = "true"
Debug.AspNetCompiler.ForceOverwrite = "true"
Debug.AspNetCompiler.FixedNames = "false"
Debug.AspNetCompiler.Debug = "True"
Release.AspNetCompiler.VirtualPath = "/localhost_54141"
Release.AspNetCompiler.PhysicalPath = "Web\"
Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_54141\"
Release.AspNetCompiler.Updateable = "true"
Release.AspNetCompiler.ForceOverwrite = "true"
Release.AspNetCompiler.FixedNames = "false"
Release.AspNetCompiler.Debug = "False"
VWDPort = "54141"
SlnRelativePath = "Web\"
StartServerOnDebug = "false"
EndProjectSection
EndProject
And Web Site is not being built.
OK, I can manually edit solution file and change/add Dev.XXXX, Test.XXX etc related settings and everything is working well until you open solution in Visual Studio and save it. Visual Studio discharges all your changes and you are faced with the same problem again.
As a workaround, I can create a copy of .sln file and manually synchronize it with the original solution. Does anybody have a better idea?
You can build the Web Site project by using different custom AspNetCompiler MSBuild task. like this:
dev.msbuild
<Project xmlns = "http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name = "PrecompileWeb">
<AspNetCompiler
VirtualPath = "/dev"
PhysicalPath = "D:\Project\WebSite1\"
TargetPath = "PrecompiledWeb\dev\"
Force = "true"
Debug = "False"
Updateable = "true"/>
</Target>
</Project>
Test.msbuild
<Project xmlns = "http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name = "PrecompileWeb">
<AspNetCompiler
VirtualPath = "/test"
PhysicalPath = "D:\Project\WebSite1\"
TargetPath = "PrecompiledWeb\test\"
Force = "true"
Debug = "False"
Updateable = "true"/>
</Target>
</Project>
build command like this:
MSBuild.exe dev.msbuild /m /p:TargetFrameworkVersion=v4.6.2;OutputPath=bin /ToolsVersion:14.0
I am trying to work out how, in my Visual Studio Extension, I can work out which of the currently loaded projects are in TFS and then the TFS server uri and location for each of these projects.
I would like to be able to do something like this:
foreach (EnvDTE.Project project in dte.Solution.Projects)
{
if (project.IsInTFS) {
var uri = project.TFSUri; // http://tfsserver:8080/tfs
var location = project.TFSLocation; // $\TeamProject\Project
}
}
Can anyone help me?
Thank you.
You can use the SourceControl2 interface to accomplish this in the following way:
foreach (Project project in dte.Solution.Projects)
{
if (dte.SourceControl.IsItemUnderSCC(project.FileName))
{
var bindings = project.GetSourceControlBindings();
var uri = bindings.ServerName;
var location = bindings.ServerBinding;
// your logic goes here
}
}
Where GetSourceControlBindings is an extension method:
public static SourceControlBindings GetSourceControlBindings(this Project project)
{
SourceControl2 sourceControl = (SourceControl2)project.DTE.SourceControl;
return sourceControl.GetBindings(project.FullName);
}
Note: you will have to a add reference to the EnvDTE80.dll assembly.
If you're interested exclusively in TFS source control, then you should add one more condition:
if (bindings.ProviderName == "{4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}")
Where that magic guid specifies the Team Foundation Source Control Provider.
I have a VS 2010 solution with a number of projects in it.
Projects reference other projects within the solution.
I have noticed that when I have a wrong project reference path in a csproj file like this:
<ProjectReference Include="..\..\..\..\WrongFolder\OtherProject.csproj">
<Project>{CD795AA6-9DC4-4451-A8BA-29BACF847AAC}</Project>
<Name>OtherProject</Name>
</ProjectReference>
Visual studio would fix this on opening the solution:
<ProjectReference Include="..\..\..\..\RightFolder\OtherProject.csproj">
<Project>{CD795AA6-9DC4-4451-A8BA-29BACF847AAC}</Project>
<Name>OtherProject</Name>
</ProjectReference>
I suppose it uses the GUID from the Project element to uniquely identify the project within the solution which allows it to fix the path.
MSBuild on the other hand doesn't seem to fix this path and building the solution fails.
Is there a way to make MSBuild fix the path or do it as a pre-build step with some other tool or command so that the solution builds correctly?
Thanks!
This is part of the VisualStudio functionality. But you can call a tool to solve the references before the build. Here is a draft code that you can elaborate:
using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
namespace FixProjectReferences
{
class Program
{
// License: This work is licensed under a Creative Commons
// Attribution-ShareAlike 3.0 Unported License.
// Author: Marlos Fabris
// Summary: Updates the project references in csproj.
// Param:
// args[0] = Main project (c:\mainProject.csproj)
// args[1] = Folder to scan other projects (c:\other)
static void Main(string[] args)
{
string mainProject = args[0];
string folder = args[1];
FileInfo mainPrjInfo = new FileInfo(mainProject);
string currentDir = Directory.GetCurrentDirectory();
// Lists all project files in the directory specified
// and scans the GUID's.
DirectoryInfo info = new DirectoryInfo(folder);
FileInfo[] projects = info.GetFiles("*.csproj",
SearchOption.AllDirectories);
Dictionary<Guid, string> prjGuids = new Dictionary<Guid, string>();
foreach (var project in projects)
{
if (project.FullName == mainPrjInfo.FullName)
continue;
Regex regex = new Regex("<ProjectGuid>(\\{.*?\\})</ProjectGuid>");
Match match = regex.Match(File.ReadAllText(project.FullName));
string guid = match.Groups[1].Value;
prjGuids.Add(new Guid(guid), project.FullName);
}
// Loads the main project and verifies if the references are valid.
// If not, updates with the correct ones from the list
// previously generated.
XmlDocument doc = new XmlDocument();
doc.Load(mainPrjInfo.FullName);
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("ns",
"http://schemas.microsoft.com/developer/msbuild/2003");
var nodes = doc.SelectNodes("//ns:ProjectReference", ns);
foreach (XmlNode node in nodes)
{
string referencePath = node.Attributes["Include"].Value;
string path = Path.Combine(mainPrjInfo.Directory.FullName,
referencePath);
if (File.Exists(path))
continue;
string projectGuid = node.SelectSingleNode("./ns:Project",
ns).InnerText;
Guid tempGuid = new Guid(projectGuid);
if (prjGuids.ContainsKey(tempGuid))
{
node.Attributes["Include"].Value = prjGuids[tempGuid];
}
}
doc.Save(mainPrjInfo.FullName);
}
}
}
I'm trying to write my first add-in for vs2010, but im struggling.
I have a assembly that generates lots of cs files. I want my plugin to add new files to the select project or if the files exist, overwrite them.
I'm having 2 problems:
When I add a new file, how do I add it to a sub folder inside the project? I seem to only be able to add to the root of the project.
If a cs file exists, how do I clear its content? Im using the EnvDTE.TextDocument & EnvDTE.EditPoint interfaces. But every time I try and iterate through the document clearing lines, I get a COM error "Exception from HRESULT: 0x80041001".
I dont want to delete the file and add a new file if I can help it. Due to the logging on source control.
textDoc = (TextDocument) document.Object("TextDocument");
EditPoint editPoint = (EditPoint)textDoc.StartPoint.CreateEditPoint();
EditPoint endPoint = (EditPoint)textDoc.EndPoint.CreateEditPoint();
editPoint.Delete(endPoint);
No looping needed and your editpoint never moves from the first position.
Well i've got a one way of doing this working.
// Get an instance of the currently running Visual Studio IDE.
var dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.10.0");
//I store the list of projects in dte2.Solution.Projects in a combobox
EnvDTE.Project project = (EnvDTE.Project)projectList.SelectedValue; //I get my projects out of a combobox
foreach (ProjectItem projectItem in project.ProjectItems)
{
Document document;
try
{
projectItem.Open();
document = projectItem.Document;
}
catch(Exception)
{
Console.WriteLine("failed to load document");
continue;
}
if (document == null)
{
continue;
}
if (document.Name == "Class1.cs") //whatever file your after
{
TextDocument editDoc = (TextDocument) document.Object("TextDocument");
EditPoint objEditPt = editDoc.CreateEditPoint();
objEditPt.StartOfDocument();
document.ReadOnly = false;
while (!objEditPt.AtEndOfDocument)
{
objEditPt.Delete(objEditPt.LineLength);
objEditPt.LineDown(1);
}
objEditPt.DeleteWhitespace(vsWhitespaceOptions.vsWhitespaceOptionsHorizontal);
objEditPt.DeleteWhitespace(vsWhitespaceOptions.vsWhitespaceOptionsVertical);
Console.WriteLine("saving file {0}", document.FullName);
document.Save(document.FullName);
}
}
I've checked out a branch of C# code from source control. It contains maybe 50 projects in various folders. There's no existing .sln file to be found.
I intended to create a blank solution to add existing solutions. The UI only lets me do this one project at a time.
Is there something I'm missing? I'd like to specify a list of *.csproj files and somehow come up with a .sln file that contains all the projects.
A PowerShell implementation that recursively scans the script directory for .csproj files and adds them to a (generated) All.sln:
$scriptDirectory = (Get-Item $MyInvocation.MyCommand.Path).Directory.FullName
$dteObj = [System.Activator]::CreateInstance([System.Type]::GetTypeFromProgId("VisualStudio.DTE.12.0"))
$slnDir = ".\"
$slnName = "All"
$dteObj.Solution.Create($scriptDirectory, $slnName)
(ls . -Recurse *.csproj) | % { $dteObj.Solution.AddFromFile($_.FullName, $false) }
$dteObj.Solution.SaveAs( (Join-Path $scriptDirectory 'All.sln') )
$dteObj.Quit()
A C# implementation that produces an executable, which creates a solution containing all unique *.csproj files from the directory and subdirectories it is executed in.
class Program
{
static void Main(string[] args)
{
using (var writer = new StreamWriter("All.sln", false, Encoding.UTF8))
{
writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 11.00");
writer.WriteLine("# Visual Studio 2010");
var seenElements = new HashSet<string>();
foreach (var file in (new DirectoryInfo(System.IO.Directory.GetCurrentDirectory())).GetFiles("*.csproj", SearchOption.AllDirectories))
{
string fileName = Path.GetFileNameWithoutExtension(file.Name);
if (seenElements.Add(fileName))
{
var guid = ReadGuid(file.FullName);
writer.WriteLine(string.Format(#"Project(""0"") = ""{0}"", ""{1}"",""{2}""", fileName, file.FullName, guid));
writer.WriteLine("EndProject");
}
}
}
}
static Guid ReadGuid(string fileName)
{
using (var file = File.OpenRead(fileName))
{
var elements = XElement.Load(XmlReader.Create(file));
return Guid.Parse(elements.Descendants().First(element => element.Name.LocalName == "ProjectGuid").Value);
}
}
}
There is extension for VS available, capable of adding all projects in selected directory (and more):
http://www.cyotek.com/blog/visual-studio-extension-for-adding-multiple-projects-to-a-solution
Use Visual Studio Extension "Add Existing Projects". It works with Visual Studio 2012, 2013, 2015, 2017.
To use the extension, open the Tools menu and choose Add Projects.
Plenty of answers here already, but none quite as clear as this powershell oneliner
Get-ChildItem -Recurse -Include *.csproj | ForEach-Object { dotnet sln add $_ }
You might be able to write a little PowerShell script or .NET app that parses all the projects' .csproj XML and extracts their details (ProjectGuid etc.) then adds them into the .sln file. It'd be quicker and less risky to add them all by hand, but an interesting challenge nonetheless.
Note: This is only for Visual Studio 2010
Found here is a cool add in for Visual Studio 2010 that gives you a PowerShell console in VS to let you interact with the IDE. Among many other things you can do using the built in VS extensibility as mentioned by #Avram, it would be pretty easy to add files or projects to a solution.
Here is a PowerShell version of Bertrand's script which assumes a Src and Test directory next to the solution file.
function GetGuidFromProject([string]$fileName) {
$content = Get-Content $fileName
$xml = [xml]$content
$obj = $xml.Project.PropertyGroup.ProjectGuid
return [Guid]$obj[0]
}
$slnPath = "C:\Project\Foo.sln"
$solutionDirectory = [System.IO.Path]::GetDirectoryName($slnPath)
$srcPath = [System.IO.Path]::GetDirectoryName($slnPath)
$writer = new-object System.IO.StreamWriter ($slnPath, $false, [System.Text.Encoding]::UTF8)
$writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00")
$writer.WriteLine("# Visual Studio 2013")
$projects = gci $srcPath -Filter *.csproj -Recurse
foreach ($project in $projects) {
$fileName = [System.IO.Path]::GetFileNameWithoutExtension($project)
$guid = GetGuidFromProject $project.FullName
$slnRelativePath = $project.FullName.Replace($solutionDirectory, "").TrimStart("\")
# Assume the project is a C# project {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
$writer.WriteLine("Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""$fileName"", ""$slnRelativePath"",""{$($guid.ToString().ToUpper())}""")
$writer.WriteLine("EndProject")
}
$writer.Flush()
$writer.Close()
$writer.Dispose()
if you open the sln file with notepad you can see the format of the file which is easy to understand but for more info take a look # Hack the Project and Solution Files .understanding the structure of the solution files you can write an application which will open all project files and write the application name ,address and GUID to the sln file .
of course I think if it's just once you better do it manually
Every answer seems to flatten the directory structure (all the projects are added to the solution root, without respecting the folder hierarchy). So, I coded my own console app that generates the solution and uses solution folders to group them.
Check out the project in GitHub
Usage
SolutionGenerator.exe --folder C:\git\SomeSolutionRoot --output MySolutionFile.sln
when you have dotnet core installed, you can execute this from git bash:
donet new sln; find . -name "*.csproj" -exec dotnet sln add {} \;
the generated solution works with csproj created for old .NET Framework.
Depends on visual studio version.
But the name of this process is "Automation and Extensibility for Visual Studio"
http://msdn.microsoft.com/en-us/library/t51cz75w.aspx
Check this out:
http://nprove.codeplex.com/
It is a free addin for vs2010 that does that and more
if the projects are under the tfs
Building on Bertrand's answer at https://stackoverflow.com/a/16069782/492 - make a console app out of this and run it in the root folder where you want the VS 2015 Solution to appear. It works for C# & VB (hey! be nice).
It overwrites anything existing but you source control, right?
Check a recently used .SLN file to see what the first few writer.WriteLine() header lines should actually be by the time you read this.
Don't worry about the project type GUID Ptoject("0") - Visual Studio will work that out and write it in when you save the .sln file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace AddAllProjectsToNewSolution
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("starting");
using (var writer = new StreamWriter("AllProjects.sln", false, Encoding.UTF8))
{
writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 14.00");
writer.WriteLine("# Visual Studio 14");
writer.WriteLine("VisualStudioVersion = 14.0.25420.1");
var seenElements = new HashSet<string>();
foreach (var file in (new DirectoryInfo(Directory.GetCurrentDirectory())).GetFiles("*.*proj", SearchOption.AllDirectories))
{
string extension = file.Extension;
if (extension != ".csproj" && extension != ".vbproj")
{
Console.WriteLine($"ignored {file.Name}");
continue;
}
Console.WriteLine($"adding {file.Name}");
string fileName = Path.GetFileNameWithoutExtension(file.Name);
if (seenElements.Add(fileName))
{
var guid = ReadGuid(file.FullName);
writer.WriteLine($"Project(\"0\") = \"{fileName}\", \"{GetRelativePath(file.FullName)} \", \"{{{guid}}}\"" );
writer.WriteLine("EndProject");
}
}
}
Console.WriteLine("Created AllProjects.sln. Any key to close");
Console.ReadLine();
}
static Guid ReadGuid(string fileName)
{
using (var file = File.OpenRead(fileName))
{
var elements = XElement.Load(XmlReader.Create(file));
return Guid.Parse(elements.Descendants().First(element => element.Name.LocalName == "ProjectGuid").Value);
}
}
// https://stackoverflow.com/a/703292/492
static string GetRelativePath(string filespec, string folder = null)
{
if (folder == null)
folder = Environment.CurrentDirectory;
Uri pathUri = new Uri(filespec);
// Folders must end in a slash
if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString()))
folder += Path.DirectorySeparatorChar;
Uri folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
}
}
}
Here is Bertrand's solution updated for
Microsoft Visual Studio Solution File, Format Version 12.00
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace AllSolutionGenerator
{
internal static class Program
{
private static void Main()
{
var dir = new DirectoryInfo(Directory.GetCurrentDirectory());
var projects = new Dictionary<string, string>();
var guid1 = Guid.NewGuid().ToString().ToUpperInvariant();
using (var writer = new StreamWriter("all.sln", false, Encoding.UTF8))
{
writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00");
foreach (var file in dir.GetFiles("*.csproj", SearchOption.AllDirectories))
{
var fileName = Path.GetFileNameWithoutExtension(file.Name);
if (!projects.ContainsKey(fileName))
{
var guid = Guid.NewGuid().ToString().ToUpperInvariant();
projects.Add(fileName, guid);
writer.WriteLine(#$"Project(""{{{guid1}}}"") = ""{fileName}"", ""{file.FullName}"",""{guid}""");
writer.WriteLine("EndProject");
}
}
writer.WriteLine("Global");
writer.WriteLine(" GlobalSection(SolutionConfigurationPlatforms) = preSolution");
writer.WriteLine(" Debug|Any CPU = Debug|Any CPU");
writer.WriteLine(" Release|Any CPU = Release|Any CPU");
writer.WriteLine(" EndGlobalSection");
writer.WriteLine(" GlobalSection(ProjectConfigurationPlatforms) = postSolution");
foreach (var (_, guid) in projects)
{
writer.WriteLine(#$" {{{guid}}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU");
writer.WriteLine(#$" {{{guid}}}.Debug|Any CPU.Build.0 = Debug|Any CPU");
}
writer.WriteLine(" EndGlobalSection");
writer.WriteLine("EndGlobal");
}
}
}
}
If you select 'Show all Files' in the Solution Explorer, you can than view all the files and folers and select them and right click to add them using 'Include in Project'.