I have the following code in a custom project based on MPF for Projects - Visual Studio 2010:
EnvDTE.Project dteProj = CurrentProject();
dteProj.ConfigurationManager.AddConfigurationRow("MyCustomConfig", "Debug", false);
var solution = dteProj.DTE.Solution as EnvDTE90.Solution3;
foreach (EnvDTE80.SolutionConfiguration2 solConfig in solution.SolutionBuild.SolutionConfigurations)
{
foreach (EnvDTE.SolutionContext solContext in solConfig.SolutionContexts)
{
if (dteProj.UniqueName != solContext.ProjectName)
continue;
//Returns E_FAIL
solContext.ConfigurationName = "MyCustomConfig";
}
}
As you can see everything is pretty straight forward. I create a new configuration for my project and want to use it in a solution context. Setting the configuration name returns E_FAIL.
Why is the assignment failing? What is the correct programmatic equivalent for selecting a project configuration for a project from the drop down in the Configuration Manager dialog box?
Thanks
Related
I'll explain the situation with an example.
Suppose I have created a Roslyn Analyzer which throws Error when Class name is TestClass. Analyzer code is as below:
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(Method, SyntaxKind.ClassDeclaration);
}
private static void Method(SyntaxNodeAnalysisContext context)
{
var node = (ClassDeclarationSyntax)context.Node;
var name = node.TryGetInferredMemberName();
if(name == "TestClass")
{
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
}
}
So i install the Analyzer nupkg in some ConsoleApp project. Console project has following code in Program.cs file
using System;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
class TestClass
{
static void test()
{
Console.WriteLine("TestClass");
}
}
}
Now if i build the ConsoleApp project in Visual Studio then i get Error as "TestClass name not to be used" which is fine.
But when i try to build the same project using msbuild command in Developer Command Prompt for VS 2017 i don't see any error from Analyzer. I want that all the errors shown in Error list in VS should be shown in Dev Cmd.
My end goal is to create a stand-alone code analysis tool project and then use MSBuildWorkspace to compile ConsoleApp project and get the analyzer errors/warnings. Part of code is as below:
var filePath = #"C:\Users\user\repos\ConsoleApp\ConsoleApp.sln";
var msbws = MSBuildWorkspace.Create();
var soln = await msbws.OpenSolutionAsync(filePath);
var errors = new List<Diagnostic>();
foreach (var proj in soln.Projects)
{
var name = proj.Name;
var compilation = await proj.GetCompilationAsync();
errors.AddRange(compilation.GetDiagnostics().Where(n => n.Severity == DiagnosticSeverity.Error).ToList());
}
var count = errors.Count();
Above code does not show errors/warnings from analyzer.
How can i achieve this?
Thanks in Advance.
To show analyzer errors/warnings during msbuild in VS Dev Cmd, you just have to pass rebuild switch for example
msbuild Tempsolution.sln /t:rebuild
And for MSBuidlWorkspace, this code worked for me. We have to manually specify the analyzer to use by using compilation.WithAnalyzer(ImmutableArray<DiagnosticAnalyzer>);.
MSBuildLocator.RegisterDefaults();
var filePath = #"C:\Users\user\repos\ConsoleApp\ConsoleApp.sln";
var msbws = MSBuildWorkspace.Create();
var soln = await msbws.OpenSolutionAsync(filePath);
var errors = new List<Diagnostic>();
foreach (var proj in soln.Projects)
{
var analyzer = proj.AnalyzerReferences.Where(alz => alz.Display.ToLower() == "Your analyzer name").FirstOrDefault();
var compilation = await proj.GetCompilationAsync();
var compWithAnalyzer = compilation.WithAnalyzers(analyzer.GetAnalyzersForAllLanguages());
var res = compWithAnalyzer.GetAllDiagnosticsAsync().Result;
errors.AddRange(res.Where(r => r.Severity == DiagnosticSeverity.Error).ToList());
}
var count = errors.Count();
How to show Analyzer errors/warnings during msbuild in VS Dev Cmd &
using MSBuildWorkspace
Actually, these warnings are from Code analysis mechanism rather than MSBuild warnings(like MSBxxx). And I think the TestClass name not to be used is just a warning(yellow mark) not an error.
In VS IDE, its environment integrates the MSBuild tool(Developer Command Prompt for VS) and Code Analyzer. Because of this, you can get the warnings in VS IDE.
However, when you use Developer Command Prompt, which is essentially a separate compilation tool for MSBuild, it doesnot have an integrated code analyzer, so you don't have this type of warning except for MSBuild warnings and errors(MSBxxx). This is also the limitation of the tool. Warning by itself does not affect the entire program.
Test
You can test it by input this in an empty console project: int a=1;(It is a code analyzer warning) and I am sure that the warning can be showed in output window in VS IDE and will not be listed in Developer Command Prompt for VS.
Suggestion
As a suggestion, you can try to treat these warnings as errors and Code Analyzer passes these warnings to the msbuild and specifies them as errors so that you can get the error in DEV.
Add these in your xxx.csproj file:
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
Although this approach breaks the build process, it is reliable and practical. And this method is very commonly used, generally used in the final production stage of the project, to exclude all errors and warnings for large projects, so as to prevent subsequent errors that may occur and be foolproof.
Then, you can use your code to build the project.
I have some T4 templates in my project. Whenever I make changes and save the tt file, it auto update the generated files. This is a template that loops all tables in a database and generates about 100+ files. So visual studio hangs for a few seconds every time I save my template and this is annoying. Is there a way to disable to "auto-refresh" function and I can manually run the template through the context menu.
Thanks!
You could delete TextTemplatingFileGenerator under "Custom Tool" in the file's Properties while you are editing it, and then put it back when you are finished.
I had a similiar issue. I found a quick work around by creating a ttinclude file (actually this was already a standard include file containing utility functions for my templates) and including it in all of my T4 templates. Then I simply created a compiler error in the include file. Thus when the generator attempted to run it would simply fail on the compile. Then when I'm ready to actually generate, I get rid of the offending code and then generate.
e.g. To cause a failure:
<#+
#
#>
To disable the failure:
<#+
//#
#>
You can also use this trick in the T4 template itself if you just want to disable the one you're working on.
Hopefully future VS versions will allow you to simply disable the auto-transform.
Since the TT is always executed (still), I found a different way to control the output when the TT is executed.
/********SET THIS TO REGENERATE THE FILE (OR NOT) ********/
var _RegenerateFile = true;
/********COS VS ALWAYS REGENERATES ON SAVE ***************/
// Also, T4VSHostProcess.exe may lock files.
// Kill it from task manager if you get "cannot copy file in use by another process"
var _CurrentFolder = new FileInfo(Host.ResolvePath(Host.TemplateFile)).DirectoryName;
var _AssemblyLoadFolder = Path.Combine(_CurrentFolder, "bin\\Debug");
Directory.SetCurrentDirectory(_CurrentFolder);
Debug.WriteLine($"Using working folder {_CurrentFolder}");
if (_RegenerateFile == false)
{
Debug.WriteLine($"Not Regenerating File");
var existingFileName = Path.ChangeExtension(Host.TemplateFile, "cs");
var fileContent = File.ReadAllText(existingFileName);
return fileContent;
}
Debug.WriteLine($"Regenerating File"); //put the rest of your usual template
Another way (what I eventually settled on) is based on reading a conditional compilation symbol that sets a property on one of the the classes that is providing the data for the T4. This gives the benefit of skipping all that preparation (and IDE lag) unless you add the REGEN_CODE_FILES conditional compilation symbol. (I guess this could also be made into a new solution configuration too. yes, this does work and removes the need for the class change below)
An example of the class i am calling in the same assembly..
public class MetadataProvider
{
public bool RegenCodeFile { get; set; }
public MetadataProvider()
{
#if REGEN_CODE_FILES
RegenCodeFile = true; //try to get this to set the property
#endif
if (RegenCodeFile == false)
{
return;
}
//code that does some degree of preparation and c...
}
}
In the TT file...
var _MetaProvider = new MetadataProvider();
var _RegenerateFile = _MetaProvider.RegenCodeFile;
// T4VSHostProcess.exe may lock files.
// Kill it from task manager if you get "cannot copy file in use by another process"
var _CurrentFolder = new FileInfo(Host.ResolvePath(Host.TemplateFile)).DirectoryName;
var _AssemblyLoadFolder = Path.Combine(_CurrentFolder, "bin\\Debug");
Directory.SetCurrentDirectory(_CurrentFolder);
Debug.WriteLine($"Using working folder {_CurrentFolder}");
if (_RegenerateFile == false)
{
Debug.WriteLine($"Not Regenerating File");
var existingFileName = Path.ChangeExtension(Host.TemplateFile, "cs");
var fileContent = File.ReadAllText(existingFileName);
return fileContent;
}
Debug.WriteLine($"Regenerating File");
I created a small extension for the EF designer that adds a new property to the property window. I did this using a vsix project (new project -> c# -> extensibility -> vsix project). When I hit F5 the experimental VS instance starts up. I create a new project, add an entity data model and add an entity. However, my break points never get hit and I don't see the property. Any ideas as to what I might be doing wrong?
public class AggregateRootValue
{
internal static XName AggregateRootElementName = XName.Get("AggregateRoot", "http://efex");
private readonly XElement _property;
private readonly PropertyExtensionContext _context;
public AggregateRootValue(XElement parent, PropertyExtensionContext context)
{
_property = parent;
_context = context;
}
[DisplayName("Aggregate Root")]
[Description("Determines if an entity is an Aggregate Root")]
[Category("Extensions")]
[DefaultValue(true)]
public string AggregateRoot
{
get
{
XElement child = _property.Element(AggregateRootElementName);
return (child == null) ? bool.TrueString : child.Value;
}
set
{
using (EntityDesignerChangeScope scope = _context.CreateChangeScope("Set AggregateRoot"))
{
var element = _property.Element(AggregateRootElementName);
if (element == null)
_property.Add(new XElement(AggregateRootElementName, value));
else
element.SetValue(value);
scope.Complete();
}
}
}
}
[Export(typeof(IEntityDesignerExtendedProperty))]
[EntityDesignerExtendedProperty(EntityDesignerSelection.ConceptualModelEntityType)]
public class AggregateRootFactory : IEntityDesignerExtendedProperty
{
public object CreateProperty(XElement element, PropertyExtensionContext context)
{
var edmXName = XName.Get("Key", "http://schemas.microsoft.com/ado/2008/09/edm");
var keys = element.Parent.Element(edmXName).Elements().Select(e => e.Attribute("Name").Value);
if (keys.Contains(element.Attribute("Name").Value))
return new AggregateRootValue(element, context);
return null;
}
}
EDIT: I put the code on Github: https://github.com/devlife/Sandbox
EDIT: After Adding the MEF component to the manifest as suggested, the extension still never loads. Here is a picture of the manifest:
So the answer, as it turns out, is in how I setup my project. I put both classes inside the project which produces the VSIX file. By simply moving those classes into another project and setting that project as the MEF Component in the manifest (and thus copying the assembly) it worked like a charm!
For VS2012, it is only needed to add Solution as MEF component also. Just add whole solution as MEF component also.
Then it works surprisingly fine.
It seems the dll built by your project isn't automatically included in the generated VSIX package, and VS2013 doesn't give you options through the IDE to change this (that I can work out, anyway).
You have to manually open the project file and alter the XML. The property to change is IncludeAssemblyInVSIXContainer.
Seen here: How to include VSIX output in it's package?
I'm currently implementing uml validation http://msdn.microsoft.com/en-us/library/ee329482.aspx,
when i debug, it opens a new experimental instance of visual studio for me to validate uml diagrams.
Is there a way to get the path of project directory selected by the user when the experimental instance of visual studio is running??
To be more clear,
project A - has VSIX and Class library components to validate uml validation. These class Library components are added to VSIX as MEF components
when i debug Project A -> new experimental instance of VS will open-> Then creating a new project (ctrl+shift+N)-> select modelling project-> browse to the directory (to store the modelling project)->Name the Project as "MYMODEL" -> then press OK
Now, In my Project A i need the path of MYMODEL. Can you please tell me how do i get that path??
Thanks in Advance,
This is a bit roundabout, but works.
You need references to EnvDTE and Microsoft.VisualStudio.Shell.Immutable.10.0 as well as the usual bits.
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
namespace Validation
{
public class MyValidationExtensions
{
[Import]
public Microsoft.VisualStudio.Shell.SVsServiceProvider ServiceProvider { get; set; }
[Export(typeof(System.Action<ValidationContext, object>))]
[ValidationMethod(
ValidationCategories.Open
| ValidationCategories.Menu)]
public void ValidateClassNames
(ValidationContext context,
// This type determines what elements
// will be validated by this method:
IModel elementToValidate)
{
IModelStore store = elementToValidate.GetModelStore();
EnvDTE.DTE dte = ServiceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
//dynamic projects = dte.ActiveSolutionProjects;
foreach (EnvDTE.Project project in dte.Solution.Projects)
{
IModelingProject mp = project as IModelingProject;
if (mp.Store == store)
{
System.Windows.Forms.MessageBox.Show(project.FullName);
}
}
}
// Add more validation methods for different element types.
}
}
I'm currently developping a Visual Studio Extension and I have a question about Options Page. Options Page allows user to save setting about your Extension. Visual Studio handle a lot of work for us.
I created the Options Page.
public class VisualStudioParameter : DialogPage
{
private string _tfsServerUrl = DefaultParameter.TfsServerUrl;
[Category("TFS Parameters")]
[DisplayName(#"Server Name")]
[Description("The URL of your TFS Server")]
public string TfsServerUrl
{
get { return _tfsServerUrl; }
set { _tfsServerUrl = value; }
}
}
First, I created a method in the Visual Studio Package to acces to the Options Page.
Okay so now, from my Package, I can easily acces to the settings.
partial class SpecFlowTfsLinkerExtensionPackage : Package : IParameter
{
....
....
public string GetTfsServerUrl()
{
return ((VisualStudioParameter) GetDialogPage(typeof (VisualStudioParameter))).TfsServerUrl;
}
}
Now, I want to be able, in another library (Another project, included in the VSIX Package), to get easily these values. I don't want to reference the Visual Studio AddIn Package in my library.
I also have Unit Test so I'm going to create an Interface. During Unit Test, I going to Mock the object.
public interface IParameter
{
string GetTfsServerUrl();
}
Do you have any idea about how I can develop a clean solution to get these parameters from another assembly ?
Do you think the better solution is to inject the AddIn dependency in my library ?
If you already developed a Visual Studio Extension, How did you encapsulated the user setting from your core assembly ?
Thanks a lot.
You can try something like that:
// Access DTE infrastructure
EnvDTE.DTE dte = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
// Access options page
var props = dte.get_Properties(#"Your Extension", "General");
var pathProperty = props.Item("TfsServerUrl");
path = pathProperty.Value as string;