string sln = path;
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution solution = workspace.OpenSolutionAsync(sln).Result;
foreach (Project pro in solution.Projects)
{
foreach (Document documents in pro.Documents)
{
SyntaxNode rootNode = documents.GetSyntaxRootAsync().Result;
SemanticModel semanticModel = documents.GetSemanticModelAsync().Result;
IEnumerable<ClassDeclarationSyntax> myClasses = rootNode.DescendantNodes().OfType<ClassDeclarationSyntax>();
string pfad = documents.FilePath; .....
What I want to do is load a solution and its projects and the documents inside those projects then I want to get the path of those files. First 2 work but there are no documents being loaded. When executing this code, the second foreach is just being skipped completely. Debugging shows that documents is null.
I've seen other projects use this type of code but it doesn't work and I have no reason why. The dots mean there is more code but that part does actually work so I just cut it out. If someone has a clue, can you please add on to my code? Much appreciated!
The possible reasons for pro.Documents returning null could be because of:
.NET standard project is not supported by MSBuild Workspace. For more information, click here.
Missing Nuget Packages
a. Microsoft.Build
b. Microsoft.Build.Utilities.Core
If none of the above solutions worked, then you can try debugging yourself by using MSBuildWorkspace.WorkspaceFailed event handler. Make sure that you subscribe to this event before calling OpenSolutionAsync method. Credits: Jason Malinowski
Related
I'm trying to use Roslyn to take an object, then explore the entire solution and find where it is declared. That part of the code has not even been started because I can't properly set up my workspace. My code is as follows:
var projectPath = #"C:\Repo\Pineapple\Pineapple.sln";
using (var workspace = MSBuildWorkspace.Create())
{
workspace.LoadMetadataForReferencedProjects = true;
var solution = workspace.OpenSolutionAsync(projectPath).Result;
ImmutableList<WorkspaceDiagnostic> diagnostics = workspace.Diagnostics;
foreach (var diagnostic in diagnostics)
{
Console.WriteLine(diagnostic.Message);
}
foreach (var project in solution.Projects)
{
foreach (var document in project.Documents)
{
Console.WriteLine(project.Name + "\t\t\t" + document.Name);
}
}
}
The second set of foreach loops had originally turned up empty prompting me to explore the diagnostics. The error message I received is listed in the title and was displayed for all files I tried to access. I'm using Visual Studio 2019 with .NET Core 3.1. If it's not already obvious, I'm relatively new to this so any help would be greatly appreciated. Thanks!
Since I don't know much about the project you are trying to load, I've used instead Nodatime and I've managed to load it successfully with your snippet by doing the following changes:
Add a reference the Microsoft.Build.Locator NuGet package
Call MSBuildLocator.RegisterDefaults(); before creating the MSBuildWorkspace
Once I've done the above steps I had to add references also to these NuGet packages:
NuGet.Frameworks
NuGet.Packaging
NuGet.ProjectModel
NuGet.Versioning
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
I am currently dealing with the error word for word:
Assemblies 'C:\Users\Jake\Desktop\AudioFileSorter\AudioFileSorter\obj\Debug\Interop.QTOControlLib.dll' and 'C:\Users\Jake\Desktop\AudioFileSorter\AudioFileSorter\libs\Interop.QTOControlLib.dll' refer to the same metadata but only one is a linked reference (specified using /link option); consider removing one of the references.
My references include several files:
AxInterop.QTOControlLib.dll
Interop.QTOControlLib.dll
Interop.QTOLibrary.dll
Interop.Shell32.dll
taglib-sharp.dll
These files are all located and referenced from a folder called libs within the base location for my project: AudioFileSorter\AudioFileSorter\libs\
An additional control reference was included as the Apple QuickTime Control 2.0 from the COM references. With the exception of this reference all other references were added by right clicking 'References' in the Solution Explorer and clicking 'Add Reference' and then browsing the libs folder to pull dll file.
Obviously, I have no idea what I am doing and I don't know how to solve it. The project worked fine yesterday and after trying to build the project to a release build everything got messed up and now I have this error. I have tried removing one of the duplicate references but then i end up just missing the reference when the app calls it during this code line:
private void SortM4PFiles(string[] files)
{
WriteLine("Begin compiling .m4p files...");
foreach (string file in files)
{
axQTControl1.URL = file;
// Create new movie object
QTOLibrary.QTMovie mov = new QTOLibrary.QTMovie();
mov = axQTControl1.Movie;
string title = mov.Annotation[(int)QTAnnotationsEnum.qtAnnotationFullName];
string artist = mov.Annotation[(int)QTAnnotationsEnum.qtAnnotationArtist];
string album = mov.Annotation[(int)QTAnnotationsEnum.qtAnnotationAlbum];
songs.Add(new Song(title, album, artist, file));
songs[songs.Count - 1].setType(".m4p");
WriteLine("Evaluated " + title);
}
// Make sure the previous .m4p is not in use
// This will prevent an IOException when the file is in use and cannot be moved
axQTControl1.URL = "";
}
Any help or explanation would be greatly appreciated. Thank you.
This was the tutorial for using the QuickTime control and reading m4p and m4a metadata.
I was trying to convert one project from packages.config to PackageReference... & I got this issue. After looking into it, I realized that, there are two references added for the same dll.
How? One from nuget & one from local COM dll. I had remove one reference to fix the issue.
Is there a way to find out the assembly name at design-time (i.e. not using reflection or runtime APIs such as System.Reflection.Assembly.GetEntryAssembly) from within Visual Studio?
The scenario requires a tool to get the assembly name that a Visual Studio project will eventually compile into.
This is like parsing the AssemblyName property of the .csproj - I am wondering if there are any APIs that can give this information reliably.
Please do not respond back with runtime APIs that use reflection - there is no assembly file present at the time I need the assembly name - just the metadata of the assembly in the csproj file.
if you are calling the tool via a post/pre-build event, this data is very easy to access.
Just go to the "project properties->Build Events" tab, then select either "edit pre-build" or "edit post-build", depending on when you want the tool to run. This should bring up an edit window with the ever helpful "Macros >>" button. Press this and you will be given a heap of macros to use and should be pretty much everything you need.
The "API" you could use is LINQ to XML after all the .csproj file is just xml. (and you can get the location of the .csproj file if you need from the solution file which for some reason is not XML but can be easily parsed)
You can use "TargetName" available in Macros for Post-build events. It will give you the assembly name for your project.
After a quick run through MSDN I found this article which might be a good start for some further research:
Accessing Project Type Specific Project, Project Item, and Configuration Properties
I think you will need to write some regular expression that will give you the value of "AssemblyTitle" attribute in AssemblyInfo.cs file.
Something like this:
public class Assembly
{
public static string GetTitle (string fileFullName) {
var contents = File.ReadAllText (fileFullName); //may raise exception if file doesn't exist
//regex string is: AssemblyTitle\x20*\(\x20*"(?<Title>.*)"\x20*\)
//loading from settings because it is annoying to type it in editor
var reg = new Regex (Settings.Default.Expression);
var match = reg.Match (contents);
var titleGroup = match.Groups["Title"];
return (match.Success && titleGroup.Success) ? titleGroup.Value : String.Empty;
}
}