In previous versions of .NET CORE (DNX) you used to be able to find get Roslyn compilation references via something like the following:
private static IEnumerable<CompilationReference> GetCompilationReferences(
ILibraryExporter libraryExporter,
ApplicationEnvironment environment)
{
var export = libraryExporter.GetAllExports(environment.ApplicationName);
return export.MetadataReferences
.OfType<IRoslynMetadataReference>()
.OrderBy(reference => reference.Name)
.Select(reference => reference.MetadataReference as CompilationReference);
}
In the latest .NET Core runtime the IRoslynMetadataReference is no longer supported, therefore not sure how to get the equivalent under the new runtime (Currently running runtime 1.0.0-2702). Refer to https://github.com/dotnet/cli
In the latest version I have managed to get access to the LibraryExporter via the following code
var appInfo = new ApplicationInfo("TestApp", Directory.GetCurrentDirectory());
var runtime = RuntimeEnvironmentExtensions.GetRuntimeIdentifier(PlatformServices.Default.Runtime);
var projectContext = ProjectContext.CreateContextForEachFramework(Directory.GetCurrentDirectory(), null, new[] { runtime }).First();
var libaryExporter = new LibraryExporter(projectContext, appInfo);
var exports = libaryExporter.GetAllExports();
var metaDataRefs = exports.SelectMany(x => x.GetMetadataReferences()).ToList();
This gives me the list of MetadataReference types however when casting these as a CompilationReference this returns null.
The reason for attempting to get compilation references out is part of a self building documentation for our code set. The compilation references are used to get the SyntaxTrees and look for summary comments on classes/properties which are then used to produce a help API for the solution.
If anyone has tried doing this sort of thing on the new platform or even has an alternate approach to getting the comments out this would be appreciated.
Related
EDIT - Looks like the initial problem only applies to .NET Core projects. So the question shifts to "What is the correct way to get the full range of project properties from .NET Core projects?"
I'm writing a Visual Studio 2019 plugin that uses some of the project configuration settings. It seems straight forward enough to get the Project object (C# here, but also C++ & others) and then spelunking the Configuration's for Property objects.
But it appears that accessing most of the properties will throw System.NotImplementedException.
Primary Question: Is there another way to access these settings - like startup arguments and other debugging configuration?
Secondary Question: Are there any good resources on this stuff? The online MS docs are a bit terse for my taste.
void ProcessCSharpProject(VSProject csProj)
{
foreach (var config in csProj.Project.ConfigurationManager.Cast<Configuration>())
{
Property debugInfoProp = config.Properties.Item("DebugInfo");
var debugInfo = debugInfoProp.Value as String; // works
Property startArgsProp = config.Properties.Item("StartArguments");
var startArgs = startArgsProp.Value as String; // NotImplemented
// Another way to access the same thing:
var configProps = config.Object as CSharpProjectConfigurationProperties6;
var startArgs2 = configProps.StartArguments; // Also NotImplemented
}
}
Thanks!
I use the IVsObjectList2.GetCategoryField2 method to retrieve different information of a type.
Now I wonder how I can retrieve C# specific information like abstract or internal modifier of a type?
The Object Browser can displays this informations.
Update 1:
I have made another attempt to get this information. Via the DynamicTypeService and the IVsHierarchy (of the project) I can get the TypeResolutionService. This can then return the Type I'm are looking for, and form the Type I get the infomrations (internal, abstract, etc.)
Unfortunately, this only works with .NET Framework projects. .NET Core projects don't work. The assumption is that .NET core projects cause problems when resolving, because the VS add-in (or Visual Studio SDK) run under .NET Framework.
var dte = Package.GetGlobalService(typeof(DTE)) as DTE2;
var serviceProvider = new ServiceProvider((Microsoft.VisualStudio.OLE.Interop.IServiceProvider)dte);
IVsSimpleObjectList2 objectList;
....
objectList.CountSourceItems(index, out var vsHierarchy, out var itemid, out var pcItems);
DynamicTypeService dynamicTypeService = (DynamicTypeService)serviceProvider.GetService(typeof(DynamicTypeService));
var typeResolutionService = dynamicTypeService.GetTypeResolutionService(hier);
var type = typeResolutionService.GetType("ObjectBuilder.ObjectBrowserTestTypes.AbstractTestClass");
More Infos here: Visual Studio Extension get all classes and interfaces metadata
I'm still looking for a solution. Does anyone have another idea?
At the end, I decided not to retrieve the information about the types via the Object-Browser or IVsObjectManager2. The reason is that I didn't get all the information I needed.
For types in the currently loaded visual studio projects I'm using the ElementClass or CodeClass class.
var service = Package.GetGlobalService(typeof(DTE)) as DTE2;
Project project = service?.Solution?.Projects.Item(0);
CodeType codeType = project.CodeModel.CodeTypeFromFullName("Full name of Type");
if (codeType.Kind == vsCMElement.vsCMElementClass && codeType is CodeClass2 codeClass)
{
// get all the information form the code class
var typeDescription = new TypeDescription();
typeDescription.FullName = codeClass.FullName;
typeDescription.ContainsGenericParameters = codeClass.IsGeneric;
typeDescription.IsAbstract = codeClass.IsAbstract;
}
For types that are in a referenced assembly I'm using Mono.Cecil. The advantage of Mono.Cecil is, that it works with .NET Framework DLLs and .NET Core DLLs. The path of the referenced assembly can be gotten via the VS-SDK.
var vsProject = project.Object as VSLangProj.VSProject;
var assemblyPath = vsProject.References.Item(0).Path;
ModuleDefinition module = Mono.Cecil.ModuleDefinition.ReadModule(assemblyPath);
foreach (TypeDefinition type in module.Types)
{
var isAbstract = type.IsAbstract;
}
Looking for some help on this. Would be much appreciated.
What I'm trying to accomplish is registering multiple versions of a type to maintain backwards compatibility on an API. This API will allow operations to be executed using older versions of the code.
My code does the following to accomplish this:
Load each version of each DLL's types into memory.
foreach (var directory in Directories)
{
assembliesToLoad.AddRange(directory.EnumerateFiles("*.dll").Select(file => Assembly.LoadFile(file.FullName)));
}
foreach (var assembly in assembliesToLoad)
{
RegisterActivityTypesFromAssembly(assembly);
}
Register them using Autofac in a loop.
var type = value.Key;
var version = $"{value.Value.Major}.{value.Value.Minor}.{value.Value.Build}";
var typeId = $"{keyValuePair.Key}#{version}";
if (type != null)
{
foreach (var interfaceType in type.GetInterfaces())
{
Builder.RegisterType(type).Named(typeId, interfaceType);
}
}
Then I load it later in the pipeline based on the version specified in the API.
var autofacTypeId = $"{_typeId}#{_version}";
_activity = Scope.ResolveNamed<IActivity>(autofacTypeId);
I've noticed this code will resolve the current version of the type no problem. Attempting to resolve older versions it fails on. What am I doing wrong here? It seems like the older version types go away for some reason, even though during the loading phase they seem to be loaded just fine after reflection.
Any help would be greatly appreciated.
I'm currently trying to generate and execute some C# code directly from a Xamarin.iOS code editor application I'm working on. I use Roslyn for all the compilation steps, but unfortunately, Mono doesn't allow you to load Assemblies at Runtime on iOS.
So, this code would typically throw a Attempting to JIT compile method while running with --aot-onlyexception.
var tree = CSharpSyntaxTree.ParseText(#"public Foo
{
public void Bar() { Console.WriteLine(""""foo"""");
}");
var compilation = CSharpCompilation.Create(
"Generated." + Guid.NewGuid(),
syntaxTrees: new[] { tree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
var result = compilation.Emit(ms);
var assembly = Assembly.Load(ms); // <- Thrown here
}
I know that Frank A. Krueger did a custom interpreter for IL for his awesome Continuous application.
I imagine having a similar approach but directly from the SemanticModel and SyntaxTrees outputted by Roslyn because I only want to support C#.
Regarding the pretty huge codebase of Roslyn, are there some bits I can pickup to base my interpreter on ?
Another question, without the possibility to generate Types dynamically, how could I represent those dynamic declared Types at Runtime ?
Thanks!
The ecosystem on iOS does not allow dynamically generated code. It's also part of legal restrictions for iOS platform.
I'm attempting to include an embedded resource into a dll that I am compiling using Roslyn. I've found something that helped put me on the right track here.
However, when I create the dll using the following code:
const string resourcePath = #"C:\Projects\...\Properties\Resources.resources";
var resourceDescription = new ResourceDescription(
"Resources.resources",
() => File.OpenRead(resourcePath),
true);
var result = mutantCompilation.Emit(file, manifestResources: new [] {resourceDescription});
I find that it will pass all of the unit tests that I have created for the project except for those that rely on the Resources file.
The error I'm getting looks like the following:
System.Resources.MissingManifestResourceException ... Make sure "[Project].Properties.Resources.resources" was correctly embedded or linked into
assembly "[Project]" at compile time, or that all the satellite assemblies required are loadable and fully signed.
The dll is supposed to be signed, and when it is emitted by roslyn it comes out with the correct public key. Also, the Resource.resx is included in my project directly in the Properties folder.
I would appreciate any help anyone could provide.
Ok, so while I was looking for answers, I came across this web page where it was commented that the resource stream was null until the he added the namespace.
So after adding the namespace I got somehting like this
const string resourcePath = #"C:\Projects\...\Properties\Resources.resources";
var resourceDescription = new ResourceDescription(
"[namespace].Resources.resources",
() => File.OpenRead(resourcePath),
true);
var result = mutantCompilation.Emit(file, manifestResources: new [] {resourceDescription});
which runs exactly like you'd expect.