I'm trying to use the new FxCop analyzers, but they're only available as NuGet packages or as VSIX extensions. I'd like to be able to run them directly, either from inside a C# program or from the command line. Anyone have any advice? Even general info on where you can find the executables for NuGet or VSIX would help.
(I know about fxcopcmd.exe, but that's the legacy version, and it works only on built .exes or .dlls. If at all possible, I need something that works before building.)
Answering my own question in case anyone else has to deal with this. I found a solution, but fair warning, it's not pretty.
I took an example C# solution from Github, loaded it up in Visual Studio, and used NuGet to install the FxCop analyzers. This installed the analyzers, and changed the solution's .csproj files to reference them. In my case, I found a copy of the analyzers in C:\users\myname.nuget\packages.
I compared the modified .csproj files to the originals, to see what changes had been made during installation. I recommend anyone following along make this comparison themselves, but in my case, the changes were:
Five Import elements at the top referencing various .props files.
An empty NuGetPackageImportStamp element.
Two new ItemGroups near the bottom, the first containing a single element named "None", the second containing various Analyzer elements referencing .dlls.
A new Target to ensure that the .props files actually existed.
I wrote a C# program that took an arbitrary solution, found all the .csproj files inside, and manually added those new XML elements to them. I skipped the one-element ItemGroup and the Target without any problems.
Ideally you would then (from inside the same C# program) call msbuild on the .sln file, save every output line matching the regex "): warning CA\d\d\d\d: " (i.e. the warnings that FxCop generated), and restore the original .csproj files. I did that all manually. Here's the code for the XML manipulation, though:
static void addAnalyzersToCsProj(string file)
{
string[] packages = new string[]
{
#"C:\users\myname\.nuget\packages\microsoft.codeanalysis.fxcopanalyzers\3.0.0\build\Microsoft.CodeAnalysis.FxCopAnalyzers.props",
#"C:\users\myname\.nuget\packages\microsoft.codeanalysis.versioncheckanalyzer\3.0.0\build\Microsoft.CodeAnalysis.VersionCheckAnalyzer.props",
#"C:\users\myname\.nuget\packages\microsoft.codequality.analyzers\3.0.0\build\Microsoft.CodeQuality.Analyzers.props",
#"C:\users\myname\.nuget\packages\microsoft.netcore.analyzers\3.0.0\build\Microsoft.NetCore.Analyzers.props",
#"C:\users\myname\.nuget\packages\microsoft.netframework.analyzers\3.0.0\build\Microsoft.NetFramework.Analyzers.props",
};
var root = XElement.Load(file);
var ns = "";
for (var i = 0; i < 5; i++)
{
XElement packageImport = new XElement(ns+"Import");
packageImport.SetAttributeValue("Project", packages[i]);
string condition = "Exists('" + packages[i] + "')";
packageImport.SetAttributeValue("Condition", condition);
root.AddFirst(packageImport);
}
var propertyGroup = root.Descendants(ns + "PropertyGroup").First();
var stamp = new XElement(ns+"NuGetPackageImportStamp", "");
propertyGroup.Elements().Last().AddAfterSelf(stamp);
var newGroup = new XElement(ns+"ItemGroup");
// do we need to include the "None Include="packages.config"" thing?
string[] libraries = new string[]
{
#"C:\users\myname\.nuget\packages\microsoft.codeanalysis.versioncheckanalyzer\3.0.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.VersionCheckAnalyzer.resources.dll",
#"C:\users\myname\.nuget\packages\microsoft.codeanalysis.versioncheckanalyzer\3.0.0\analyzers\dotnet\Microsoft.CodeAnalysis.VersionCheckAnalyzer.dll",
#"C:\users\myname\.nuget\packages\microsoft.codequality.analyzers\3.0.0\analyzers\dotnet\cs\Humanizer.dll",
#"C:\users\myname\.nuget\packages\microsoft.codequality.analyzers\3.0.0\analyzers\dotnet\cs\Microsoft.CodeQuality.Analyzers.dll",
#"C:\users\myname\.nuget\packages\microsoft.codequality.analyzers\3.0.0\analyzers\dotnet\cs\Microsoft.CodeQuality.CSharp.Analyzers.dll",
#"C:\users\myname\.nuget\packages\microsoft.netcore.analyzers\3.0.0\analyzers\dotnet\cs\Microsoft.NetCore.Analyzers.dll",
#"C:\users\myname\.nuget\packages\microsoft.netcore.analyzers\3.0.0\analyzers\dotnet\cs\Microsoft.NetCore.CSharp.Analyzers.dll",
#"C:\users\myname\.nuget\packages\microsoft.netframework.analyzers\3.0.0\analyzers\dotnet\cs\Microsoft.NetFramework.Analyzers.dll",
#"C:\users\myname\.nuget\packages\microsoft.netframework.analyzers\3.0.0\analyzers\dotnet\cs\Microsoft.NetFramework.CSharp.Analyzers.dll",
};
foreach (string lib in libraries)
{
XElement analyzer = new XElement(ns+"Analyzer");
analyzer.SetAttributeValue("Include", lib);
newGroup.AddFirst(analyzer);
}
Console.WriteLine(root.Elements().Last().ToString());
root.Elements().Last().AddAfterSelf(newGroup);
root.Save(file, SaveOptions.None);
// and do we need to include the error checking target?
}
As far as I can tell, it works, though I have no idea what would happen if you tried to do it on a solution that already has the analyzers installed normally.
Running the FxCop analyzers through msbuild seems inefficient, but I haven't found a better way to do it. They look like they're built to only work within a compiler. I hope I'm wrong, and I would still appreciate any advice on how to run the analyzers automatically without having to build the whole project.
I am currently trying to get a list of 3rdParties shipped with each product and have come across the NDepend API. Based on the research I have done, it seems like you feed in a solution file and out comes a list of DLLs and EXE's associated with that solution. So far I have tried:
static void Main(string[] args)
{
var ndependServicesProvider = new NDependServicesProvider();
var projectManager = ndependServicesProvider.ProjectManager;
var visualStudioManager = ndependServicesProvider.VisualStudioManager;
var projPath = "C:\\code\\depot\\Captiva\\IA\\EIM\\_Trunk\\Src\\BuildInputAccel.Installers.sln";
var sln = projPath.ToAbsoluteFilePath();
var vsSlnOrProjFilePaths = new List<IAbsoluteFilePath> { sln };
var assembliesFilePath = (from vsSlnOrProjFilePath in vsSlnOrProjFilePaths
from assembliesFilePathTmp in visualStudioManager.GetAssembliesFromVisualStudioSolutionOrProject(vsSlnOrProjFilePath)
select assembliesFilePathTmp).Distinct().ToArray();
IProject project = projectManager.CreateTemporaryProject(assembliesFilePath, TemporaryProjectMode.Temporary);
project.CodeToAnalyze.SetApplicationAssemblies(assembliesFilePath);
projectManager.SaveProject(project);
IAnalysisResult analysisResult = project.RunAnalysis();
Console.Write(analysisResult.CodeBase);
}
And have gotten a An unhandled exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll
Does anyone know what I am doing wrong here. I simply want to output a list of dll's and exes associated with each project within a solution. PS: I am very new to C# so sorry if this seems trivial!
At the end of the NDepend API getting started page you'll find instruction about what to do.
Actually can use the integrated Code Querying LINQ (CQLinq) facility to query live 3rd party assemblies referenced and their usage.
1) from the NDepend start page > Analyze VS solution
2) choose your solution
3) run analysis
Then you can just edit this code query:
from a in ThirdParty.Assemblies
select new { a, a.AssembliesUsingMe }
et voilĂ
If some third-party assemblies are missing it is because they haven't been resolved at analysis time. Look at analysis error list and update the list of folder where NDepend will look for assemblies in NDepend Project Properties > Code to Analyze > Directories
You have a large Visual Studio Solution with dozens of project files in it. How would you verify that all the projects follow certain rules in their property settings, and enforce these rules if a new project is added. For example check that all projects have:
TargetFrameworkVersion = "v4.5"
Platform = "AnyCPU"
WarningLevel = 4
TreatWarningsAsErrors = true
OutputPath = $(SolutionDir)bin
SignAssembly = true
AssemblyName = $(ProjectFolderName)
I know two methods myself that I will add in an answer below, but I was wondering how people go about doing this type of project test. I'm especially interested to learn about available solutions such as libraries or build tasks for this rather than having to have to invent something new or write it from scratch.
*.sln files are plain text and easily parsable, and *.*proj files are xml.
You can add a dummy project with a prebuild step that parses the sln to retrieve all of the project files, validate their settings, print a report, and fail the build if necessary.
Also, you should check this post to ensure the prebuild step is always executed. Essentially, you specify a blank output in the custom build step to force a rebuild.
The following list identifies the key file types that are automatically added to VSS when a solution is added to source control by using the Visual Studio .NET integrated development environment (IDE):
Solution files (.sln). The key items maintained within these files include a list of constituent projects, dependency information, build configuration details, and source control provider details.
Project files (.csproj or *.vbproj). The key items maintained within these files include assembly build settings, referenced assemblies (by name and path), and a file inventory.
Application configuration files. These are configuration files based on Extensible Markup Language (XML) used to control various aspects of your project's run time behavior.
Use a Single Solution Model Whenever Possible an
Also see : https://msdn.microsoft.com/en-us/library/ee817677.aspx,
https://msdn.microsoft.com/en-us/library/ee817675.aspx
AND For CONTINUOUS INTEGRATION :
there are many tools available like MSBuild, Jenkins, Apache's Continuum, Cruise Control (CC), and Hudson(plugin can be extended to c#)
This is what I have myself:
One way to do this is to create an MSBuild target with error conditions:
<Error Condition="'$(TreatWarningsAsErrors)'!='true'" Text="Invalid project setting" />
I like this approach because it is integrated with MSBuild and gives you early errors, however, you have to modify every project to import it in them or get all your team members to use a special command prompt with environment variables that will inject custom pre-build steps into your projects during the build, which is a pain.
The second approach I know is to use some library like VSUnitTest which provides an API to project properties that you can test against. VSUnitTest is currently not open source and unlisted from the NuGet service.
You could write some code to open the the solution as a text file to identify all of the csproj files referenced, in turn opening each of these as xml files, and then writing unit tests to ensure specific nodes of the project match what you expect.
It's a quick and dirty solution, but works for CI and gives you the flexibility to ignore nodes you don't care about. It actually sounds kinda useful. I have a solution with 35 projects I'd like to scan too.
Let's try something completely different: you could ensure that they are consistent by construction by generating them from a template or by using a build generation tool such as CMake. This might be simpler than attempting to make them consistent after the fact.
In our work we use a powershell script that checks project settings and modified them if they are incorrect. For example, we remove Debug configuration this way, disable C++ optimization and SSE2 support. We run it manually, but definitely it is possible to run it automatically, e.g. as pre\post build step.
Below the example:
`function Prepare-Solution {
param (
[string]$SolutionFolder
)
$files = gci -Recurse -Path $SolutionFolder -file *.vcxproj | select - ExpandProperty fullname
$files | %{
$file = $_
[xml]$xml = get-content $file
#Deleting Debug configurations...
$xml.Project.ItemGroup.ProjectConfiguration | ?{$_.Configuration -eq "Debug"} | %{$_.ParentNode.RemoveChild($_)} | Out-Null
$xml.SelectNodes("//*[contains(#Condition,'Debug')]") |%{$_.ParentNode.RemoveChild($_)} | Out-Null
if($xml.Project.ItemDefinitionGroup.ClCompile) {
$xml.Project.ItemDefinitionGroup.ClCompile | %{
#Disable SSE2
if (-not($_.EnableEnhancedInstructionSet)){
$_.AppendChild($xml.CreateElement("EnableEnhancedInstructionSet", $xml.DocumentElement.NamespaceURI)) | Out-Null
}
if($_.ParentNode.Condition.Contains("Win32")){
$_.EnableEnhancedInstructionSet = "StreamingSIMDExtensions"
}
elseif($_.ParentNode.Condition.Contains("x64")) {
$_.EnableEnhancedInstructionSet = "NotSet"
} else {
Write-Host "Neither x86 nor x64 config. Very strange!!"
}
#Disable Optimization
if (-not($_.Optimization)){
$_.AppendChild($xml.CreateElement("Optimization", $xml.DocumentElement.NamespaceURI)) | Out-Null
}
$_.Optimization = "Disabled"
}
}
$xml.Save($file);
} }`
A file is an assembly if and only if it is managed, and contains an assembly entry in its metadata. For more information on assemblies and metadata, see the topic Assembly Manifest.
How to manually determine if a file is an assembly
Start the Ildasm.exe (IL Disassembler).
Load the file you wish to test.
If ILDASM reports that the file is not a portable executable (PE) file, then it is not an assembly. For more information, see the topic How to: View Assembly Contents.
How to programmatically determine if a file is an assembly
Call the GetAssemblyName method, passing the full file path and name of the file you are testing.
If a BadImageFormatException exception is thrown, the file is not an assembly.
This example tests a DLL to see if it is an assembly.
class TestAssembly
{
static void Main()
{
try
{
System.Reflection.AssemblyName testAssembly = System.Reflection.AssemblyName.GetAssemblyName(#"C:\Windows\Microsoft.NET\Framework\v3.5\System.Net.dll");
System.Console.WriteLine("Yes, the file is an assembly.");
}
catch (System.IO.FileNotFoundException)
{
System.Console.WriteLine("The file cannot be found.");
}
catch (System.BadImageFormatException)
{
System.Console.WriteLine("The file is not an assembly.");
}
catch (System.IO.FileLoadException)
{
System.Console.WriteLine("The assembly has already been loaded.");
}
}
}
// Output (with .NET Framework 3.5 installed):
// Yes, the file is an assembly.
Framework is the highest installed version, SP is the service pack for that version.
RegistryKey installed_versions = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\NET Framework Setup\NDP");
string[] version_names = installed_versions.GetSubKeyNames();
//version names start with 'v', eg, 'v3.5' which needs to be trimmed off before conversion
double Framework = Convert.ToDouble(version_names[version_names.Length - 1].Remove(0, 1), CultureInfo.InvariantCulture);
int SP = Convert.ToInt32(installed_versions.OpenSubKey(version_names[version_names.Length - 1]).GetValue("SP", 0));
For .Net 4.5
using System;
using Microsoft.Win32;
...
private static void Get45or451FromRegistry()
{
using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\\")) {
int releaseKey = Convert.ToInt32(ndpKey.GetValue("Release"));
if (true) {
Console.WriteLine("Version: " + CheckFor45DotVersion(releaseKey));
}
}
}
...
// Checking the version using >= will enable forward compatibility,
// however you should always compile your code on newer versions of
// the framework to ensure your app works the same.
private static string CheckFor45DotVersion(int releaseKey)
{
if (releaseKey >= 393273) {
return "4.6 RC or later";
}
if ((releaseKey >= 379893)) {
return "4.5.2 or later";
}
if ((releaseKey >= 378675)) {
return "4.5.1 or later";
}
if ((releaseKey >= 378389)) {
return "4.5 or later";
}
// This line should never execute. A non-null release key should mean
// that 4.5 or later is installed.
return "No 4.5 or later version detected";
}
For similar purposes we use custom MSBuild fragments with common properties that we want to share between the projects, like this (build.common.props file):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<PlatformToolset>v90</PlatformToolset>
<OutputPath>$(SolutionDir)..\bin\$(PlatformPath)\$(Configuration)\</OutputPath>
<!-- whatever you need here -->
</PropertyGroup>
</Project>
And then we just include this fragment to real VS projects we want to apply these properties to:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CommonProps>$(SolutionDir)..\Build\build.common.props</CommonProps>
</PropertyGroup>
<Import Project="$(CommonProps)" />
<!-- the rest of the project -->
</Project>
We handle a lot of things using this approach:
common properties, as you mentioned
static analysis (FxCop, StyleCop)
digital sign of assemblies
etc.
The only disadvantage that you need to include these MSBuild fragments into each project file, but once you do that, you have all the benefits of modular build system that is easy to manage and update.
You could go the search & replace Regex way with a handwritten C#, Script, powershell or similar. But it has the following problems:
Difficult to read (Read your pretty regex in three or more months)
Difficult to enhance(New regex for new search/replace/check feature)
Easy to break (a new release/format of ms build project or a not forecast tag may not work)
Harder to test (you must check that no unintended match occurs)
Difficult to maintain (because of the above)
and the following advantages:
Not doing any extra validation which (may) let it work on any kind of project (mono or visual).
Doesn't care about \r :)
The best could be to use the Microsoft.Build.Evaluation
and build a C# tool which does all your testing/checking/fix and so on.
I've done a command line tool that use a sourcefile list (used by Mono) and update sources of csproj and another which dumps on console the csproj content. It was easy to do, pretty straightforward and easy to test also.
However, it may fail (as I've experienced it) on projects modified by "non" Ms tool (like Mono Studio) or because of missing \r....
Anyway, you can always handle it with an exception catch and a good message.
Here a sample by using Microsoft.Build.dll (don't use Microsof.Build.Engine as it is obsolete):
using System;
using Microsoft.Build.Evaluation;
internal class Program
{
private static void Main(string[] args)
{
var project = new Project("PathToYourProject.csproj");
Console.WriteLine(project.GetProperty("TargetFrameworkVersion", true, string.Empty));
Console.WriteLine(project.GetProperty("Platform", true, string.Empty));
Console.WriteLine(project.GetProperty("WarningLevel", true, string.Empty));
Console.WriteLine(project.GetProperty("TreatWarningsAsErrors", true, "false"));
Console.WriteLine(project.GetProperty("OutputPath", false, string.Empty));
Console.WriteLine(project.GetProperty("SignAssembly", true, "false"));
Console.WriteLine(project.GetProperty("AssemblyName", false, string.Empty));
Console.ReadLine();
}
}
public static class ProjectExtensions
{
public static string GetProperty(this Project project, string propertyName, bool afterEvaluation, string defaultValue)
{
var property = project.GetProperty(propertyName);
if (property != null)
{
if (afterEvaluation)
return property.EvaluatedValue;
return property.UnevaluatedValue;
}
return defaultValue;
}
}
I also faced this issue and created a small solution that creates a csv file with details to identifies the inconsistences. You can look at it in this url
https://github.com/gdlmanuv/VSProjectConsistencyChecker
is there a way to subscribe to Test Explorer events in visual studio extension?
I didn't find anything like that in DTE2 interface. My goal is to trigger some function from extension when Test run completed (for the test that were ran from Test Explorer)
Thank you!
Thanks 280Z28 for your answer. Working code by using application object DTE:
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.TestWindow.Extensibility;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.TestTools.Execution;
public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
Microsoft.VisualStudio.OLE.Interop.IServiceProvider InteropServiceProvider = application as Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
_ServiceProvider = new ServiceProvider(InteropServiceProvider);
_ComponentModel = (IComponentModel)_ServiceProvider.GetService(typeof(SComponentModel));
_OperationState = _ComponentModel.GetService<IOperationState>();
_OperationState.StateChanged += _OperationState_StateChanged;
}
void _OperationState_StateChanged(object sender, OperationStateChangedEventArgs e)
{
}
It is also possible to access currently discovered test by ITestsService.
_TestsService = _ComponentModel.GetService<Microsoft.VisualStudio.TestWindow.Extensibility.ITestsService>();
var GetTestTask = _TestsService.GetTests();
GetTestTask.ContinueWith(Task =>
{
var DiscoveredTests = Task.Results.ToList();
});
The interfaces you need are available through MEF in the Microsoft.VisualStudio.TestWindow.Interfaces.dll assembly.
You need to expose your extension through MEF and [Import] an instance of IOperationState, or use the IComponentModel interface (returned for the SComponentModel service) to access the IOperationState. From there, you want to add an event handler to the IOperationState.StateChanged event, and look for the State property to include the TestOperationStates.TestExecutionFinished flag.
I'm terribly sorry for the lack of links, but I couldn't find any information about this in MSDN.
Edit: Two remarks about compatibility.
This is only available in Visual Studio 2012 and newer.
The necessary assembly (mentioned above) has a different strong name in the two versions of Visual Studio, and there is no bindingRedirect in Visual Studio 2013. What this means is you will be forced to deploy separate extensions for Visual Studio 2012 and Visual Studio 2013, or get "clever" about the way you dynamically load your extension code (the latter is way beyond the scope of this answer, but I've used it for some cases like Inheritance Margin extension that requires access to version-specific IntelliSense resources).
Sample VS 2017
A sample using MEF and the ITestContainerDiscoverer exported type. But be aware this may be gone in VS 2019!
[Export(typeof(ITestContainerDiscoverer))]
[Export(typeof(Testything))]
internal class Testything : ITestContainerDiscoverer
{
[ImportingConstructor]
internal Testything([Import(typeof(IOperationState))]IOperationState operationState)
{
operationState.StateChanged += OperationState_StateChanged;
}
public Uri ExecutorUri => new Uri("executor://PrestoCoverageExecutor/v1");
public IEnumerable<ITestContainer> TestContainers
{
get
{
return new ITestContainer[0].AsEnumerable();
}
}
public event EventHandler TestContainersUpdated;
private void OperationState_StateChanged(object sender, OperationStateChangedEventArgs e)
{
if (e.State == TestOperationStates.TestExecutionFinished)
{
var s = e.Operation;
}
}
}
Some more things could be found here
https://www.fuget.org/packages/Microsoft.VisualStudio.TestWindow.Interfaces/
I've used a tutorial (http://support.microsoft.com/kb/837908, method 1) to generate two projects. Project a has a reference to project b.
project a:
new System.EnterpriseServices.Internal.Publish().GacRemove(string.Format(#"C:\Users\[me]\Documents\Visual Studio 2010\Projects\MyAssembly1\MyAssembly1\bin\Debug\MyAssembly1.dll"));
new System.EnterpriseServices.Internal.Publish().GacInstall(string.Format(#"C:\Users\[me]\Documents\Visual Studio 2010\Projects\MyAssembly1\MyAssembly1\bin\Debug\MyAssembly1.dll"));
MyAssembly1.Class1 obj1 = new MyAssembly1.Class1();
MessageBox.Show(obj1.HelloWorld());
project b:
public string HelloWorld()
{
return "1";
}
when I perform the following:
change "HelloWorld" method in project b to return "2" (instead of "1").
build project b
build project a and run it
I get message box with "1" as text, and the GAC doesn't always update itself.
What is the simplest way to update project b and see it on project a?
Firstly, changing the GAC is unlikely to help an already running process. In this specific case, note that JIT happens per-method before the method is executed. Which means MyAssembly1 is resolved and loaded before the remove/install step. However, that doesn't change the fact that this is very unlikely to be a good approach.