I'm trying to build some kind of custom Configuration Manager for Visual Studio 2013. I've created a VSPackage MenuCommand and currently run it in an visual studio experimental instance. To get the projects of the current solution i use this:
public static EnvDTE80.DTE2 GetActiveIDE()
{
// Get an instance of currently running Visual Studio IDE.
var dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.12.0");
return dte2;
}
public static IList<Project> Projects()
{
Projects projects = GetActiveIDE().Solution.Projects;
List<Project> list = new List<Project>();
var item = projects.GetEnumerator();
while (item.MoveNext())
{
var project = item.Current as Project;
if (project == null)
{
continue;
}
list.Add(project);
}
return list;
}
When I try to access the ConfigurationManager of a project, I get a NullRefernceException:
var projects = Projects().OrderBy(p => p.Name).ToList();
foreach (var project in projects)
{
DataRow row = solutionConfigurationData.NewRow();
row[0] = project.Name;
row[1] = project.ConfigurationManager.ActiveConfiguration.ConfigurationName;
row[2] = project.ConfigurationManager.ActiveConfiguration.PlatformName;
}
The COM-Object itself (project) works fine, because if comment out row[1] and row[2] I get a list of all project names.
Don't know how else to get the project configuration, because all the posts i found use the configuration manager.
1) Do not use Marshal.GetActiveObject to get the EnvDTE instance where your extension is loaded, it could return another running instance. Use GetService(typeof(EnvDTE.DTE))
2) Navigate the projects of the solution recursively, not linearly. See HOWTO: Navigate the files of a solution from a Visual Studio .NET macro or add-in.
3) EnvDTE.Project.ConfigurationManager can return null if the project doesn't support configuration. For C# and VB.NET project it should work, though. C++ projects use a different project model (VCConfiguration). For other projects maybe don't even support configuration programmatically.
Related
I am trying to figure out which project is enabled/disabled in respective build configuration/platform setup. Where could I find this "project.BuildsInCurrentConfiguration" information please?
var properties = new Dictionary<string, string>
{
{ "Configuration", "Debug" },
{ "Platform", "x86"}
};
MSBuildWorkspace workspace = MSBuildWorkspace.Create(properties);
workspace.LoadMetadataForReferencedProjects = true;
Solution solution = workspace.OpenSolutionAsync("someSolution.sln").Result;
foreach (Project project in solution.Projects)
Console.Out.WriteLine($"{project.OutputFilePath} is enabled in this build setup: {project.BuildsInCurrentConfiguration}");
workspace.CloseSolution();
I would have thought I wouldn't be offered the projects that are not part of the picked configuration/platform, but solution.Projects shows me all of them regardless build setup.
I don't think Roslyn really has most of that information right now (I'm not sure if it ever would; but I would hope it would). I don't see anything related to a "configuration" for a project with the Roslyn APIs for example. That seems to be delegated to the DTE interfaces. You can get at platform type in a Roslyn project, so conceptually you could only get projects that would apply to a given type of build:
var rolsynProjects = solution.Projects
.Where(p => p.CompilationOptions.Platform == Platform.X86);
but, things like "DEBUG" configuration seem to only be available via DTE--which isn't that hard to get at. e.g.
var project = DTE.Solution.Projects
.Where(p=>p.FullName == rolsynProjects.First().FilePath).FirstOrDefault();
And from that VS project, you can get at its ConfigurationManager
I am creating an app that will add some files and folders to an existing project which is loaded in Visual studio. That works but it will always pop up a message telling the user (me) to refresh the project to show the new files.
When using Entity Framework and adding a migration it will add a file to a project that is currently loaded and it doesn't ask the user. I would like to be able to do the same thing.
Is this possible? If someone doesn't know the answer do they know how I might delve into EF and add migration to see how they do it?
Here is the code that I am using to edit the project file:
using Microsoft.Build.Evaluation;
internal class ProjectFileHandler
{
private void AddMigrationFolder(Project project, string projectFileLocation, string name)
{
var loc = Path.Combine(projectFileLocation.Substring(0, projectFileLocation.LastIndexOf("\\")), name);
project.AddItem("Folder", loc);
}
internal void AddMigrationFile(string projectfileLocation, string migrationFolderName, string fileLocation)
{
var project = new Project(projectfileLocation);
AddMigrationFolder(project, projectfileLocation, migrationFolderName);
project.AddItem("Compile", fileLocation);
project.Save();
}
}
Pretty old, but I'll leave a comment. Maybe it helps someone else.
The problem is the "project.Save();"
The solution recognizes this as an "external" change.
Try your code without this line.
Even older, but I had to solve this problem today, so for what it's worth, here's what I did.
Firstly, as far as I can tell, you can't update a project using Microsoft.Build.Evaluation without Visual Studio prompting the user to reload it. You have to call project.Save() to write your updates to the project XML, and Visual Studio 'sees' the file change and displays the prompt.
What you can do - and this looks to be what Entity Framework does, albeit with Powershell - is use EnvDTE instead of Microsoft.Build.Evaluation. EnvDTE is available on NuGet and the latest version supports .NET Framework 4.5 and .NET Standard 2, so it should be pretty widely compatible.
With EnvDTE you get a reference to a Project object and call AddFromFile(absoluteFilePath) - there are also other Add() methods if needed.
If you have the full file path to your project, you can get a project file using:
using System.Linq;
using System.Runtime.InteropServices;
using EnvDTE;
var dte = (DTE)Marshal.GetActiveObject("VisualStudio.DTE");
var project = dte
.Solution
.EnumerateProjects()
.First(p => p.FullName == fullPathToProject);
...where EnumerateProjects() is the following extension method:
using EnvDTE;
using System.Collections;
using System.Collections.Generic;
internal static class EnvDteExtensions
{
public static IEnumerable<Project> EnumerateProjects(
this Solution solution)
{
return Enumerate(solution.Projects);
}
private static IEnumerable<Project> Enumerate(IEnumerable items)
{
foreach (var item in items)
{
var candidateProject = item;
if (candidateProject is ProjectItem projectItem &&
projectItem.SubProject != null)
{
candidateProject = projectItem.SubProject;
}
if (!(candidateProject is Project project))
{
continue;
}
yield return project;
try
{
if (project.ProjectItems == null)
{
continue;
}
}
catch
{
continue;
}
foreach (var subProject in Enumerate(project.ProjectItems))
{
yield return subProject;
}
}
}
}
A Project in EnvDTE might actually be a project, or it might be a solution folder containing projects - the candidateProject is ProjectItem projectItem && projectItem.SubProject != null check handles the latter scenario.
EnvDTE's Project.AddFromFile() pops the file into Solution Explorer with no project reload prompt.
Finally, AddFromFile() seems to be idempotent, so you can spam the same file into the project without worrying if it already exists.
How can I create a command in a VSPackage that creates a new solution with a new project (C# Class Library) in it, containing 1 .cs file?
It is not very straighforward but there is an interesting guide on MSDN that explains how to do it. It is for an Add-in but in a VSPackage you have the same set of Visual Studio DTE objects available (the DTE application).
You can define a method that uses GetProjectTemplate and AddFromTemplate to create two console projects. You can define in the method Initialize of your VSPackage class a OLE menu command (if that is what you are looking for):
protected override void Initialize()
{
//// Create the command for the menu item.
var aCommand = new CommandID(GuidList.GuidCmdSet, (int)PkgCmdIdList.CmdId);
var menuItemEnable = new OleMenuCommand((s, e) => createProjectsFromTemplates(), aCommand);
}
And then define a method associated to the command (createProjectsFromTemplates in this case) that creates a solution with a project:
private DTE2 _mApplicationObject;
public DTE2 ApplicationObject
{
get
{
if (_mApplicationObject != null) return _mApplicationObject;
// Get an instance of the currently running Visual Studio IDE
var dte = (DTE)GetService(typeof(DTE));
_mApplicationObject = dte as DTE2;
return _mApplicationObject;
}
}
public void createProjectsFromTemplates()
{
try
{
// Create a solution with two projects in it, based on project
// templates.
Solution2 soln = (Solution2)ApplicationObject.Solution;
string csTemplatePath;
string csPrjPath = "C:\\UserFiles\\user1\\addins\\MyCSProject";
// Get the project template path for a C# console project.
// Console Application is the template name that appears in
// the right pane. "CSharp" is the Language(vstemplate) as seen
// in the registry.
csTemplatePath = soln.GetProjectTemplate(#"Windows\ClassLibrary\ClassLibrary.vstemplate",
"CSharp");
System.Windows.Forms.MessageBox.Show("C# template path: " +
csTemplatePath);
// Create a new C# console project using the template obtained
// above.
soln.AddFromTemplate(csTemplatePath, csPrjPath, "New CSharp
Console Project", false);
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show("ERROR: " + ex.Message);
}
}
For Visual Studio versions after 10.0 the zip for template projects is not available anymore. The .vstemplate must be referenced and it is possible to find all the project templates under the folder:
C:\Program Files (x86)\Microsoft Visual Studio 1x.0\Common7\IDE\ProjectTemplates\
More info on this MSDN link.
The method should create a solution with one C# project based on the c# project template (e.g. containing class1.cs as initial file).
You can define your own template as well if you wish and create a solution based on that custom template. Here is a guide from MSDN on how to create custom templates.
hope it helps.
I am working on a small Visual Studio extension that acts on projects in a solution based on if they are set to build in the active build configuration or not. The problem I am having is that I cannot figure out how to determine what those projects are.
I have implemented IVsUpdateSolutionEvents, in which I implement OnActiveProjectCfgChange. I can get Visual Studio to enter the code block when I change configurations, and I have been able to get it to do many of the things that I would like to do, but without being able to determine what projects should be built in the active configuration, I am dead in the water.
My implementation so far is:
public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy)
{
var activeProjects = new HashSet<string>(); // TODO: Get projects in active configuration
foreach (Project project in _dte.Solution.Projects)
{
if (project.Kind != "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" // C#
&& project.Kind != "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}" // VB
&& project.Kind != "{13B7A3EE-4614-11D3-9BC7-00C04F79DE25}" // VSA
)
continue;
IVsHierarchy projectHierarchy;
_solutionService.GetProjectOfUniqueName(project.UniqueName, out projectHierarchy);
if (activeProjects.Contains(project.UniqueName))
{
// Project is to be built
}
else
{
// Project is not to be built
}
return VSConstants.S_OK;
}
}
What I need to do is figure out how to fill in the HashSet at the beginning of the function. (Marked with TODO). I have searched and searched, but I have not found what I need.
Does anybody have any references to documentation or sample code that might help me move forward?
You can use SolutionContext.ShouldBuild property
foreach (SolutionContext solutionContext in _applicationObject.Solution.SolutionBuild.ActiveConfiguration.SolutionContexts)
{
if (solutionContext.ShouldBuild)
activeProjects.Add(solutionContext.ProjectName);
}
I am currently trying to create an addin for Visual Studio 2008 that will list all files which are not excluded from the current build configuration.
I currently have test C++ console application that has 10 files, 2 of which are "Excluded From Build". This is a property that will allow a specific file to be excluded from a specific configuration (i.e. Debug or Release). This property is located when you right click on a file in the solution explorer and select Properties->Configuration Properties->General->Excluded From Build
At the moment I have the following code that will loop though all project files and get the properties for each file.
foreach (Project theProject in _applicationObject.Solution.Projects)
{
getFiles(theProject.ProjectItems);
}
private void getFiles(ProjectItems theItems)
{
foreach (ProjectItem theItem in theItems)
{
string theItemName = theItem.Name;
foreach (Property theProp in theItem.Properties)
{
string thePropName = theProp.Name;
}
getFiles(theItem.ProjectItems);
}
}
The issue I am having is that I cant seem to find the "Excluded From Build" property. I cannot find very good documentation on what properties are listed where. Where is this Excluded From Build property located within the _applicationObject object?
I'm not familiar with the Visual Studio object model, but in the documentation for VS2005 the following objects have an ExcludedFromBuild property:
VCFileConfiguration
VCFileConfigurationProperties
VCPreBuildEventTool
VCPreLinkEventTool
VCPostBuildEventTool
VCWebDeploymentTool
Hopefully this will lead you down the right path.