InternalsVisibleTo attribute ignored when compiling solution via Roslyn - c#

So I am developing a Roslyn-based frontend compiler that parses a C# solution, performs rewriting on the syntax trees to desugar some constructs of my DSL, and then uses the Roslyn APIs to compile and emit executables/dlls. This last part, given a compilation, is done very simply like this (some details omitted for clarity):
var compilation = project.GetCompilationAsync().Result;
var compilationOptions = new CSharpCompilationOptions(outputKind,
compilation.Options.ModuleName, compilation.Options.MainTypeName,
compilation.Options.ScriptClassName, null,
compilation.Options.OptimizationLevel, compilation.Options.CheckOverflow,
false, compilation.Options.CryptoKeyContainer,
compilation.Options.CryptoKeyFile, compilation.Options.CryptoPublicKey,
compilation.Options.DelaySign, Platform.AnyCpu,
compilation.Options.GeneralDiagnosticOption,
compilation.Options.WarningLevel,
compilation.Options.SpecificDiagnosticOptions,
compilation.Options.ConcurrentBuild,
compilation.Options.XmlReferenceResolver,
compilation.Options.SourceReferenceResolver,
compilation.Options.MetadataReferenceResolver,
compilation.Options.AssemblyIdentityComparer,
compilation.Options.StrongNameProvider);
var targetCompilation = CSharpCompilation.Create(assemblyFileName,
compilation.SyntaxTrees, compilation.References,
compilationOptions);
EmitResult emitResult = null;
using (var outputFile = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
emitResult = targetCompilation.Emit(outputFile);
if (emitResult.Success)
{
return fileName;
}
}
So everything works perfectly fine, until I tried to compile a solution where a project A declares some internal classes/fields, and then uses the [assembly: InternalsVisibleTo("...")] attribute (in the AssemblyInfo.cs file) to give visibility of these internals to another project B.
Directly compiling using visual studio works perfectly fine and allows B to see these internal declarations of A. However, when I try to compile through my tool using the Roslyn APIs, it is like the InternalsVisibleTo attribute is completely ignored, and I am getting back errors, such as:
error CS0122: ... is inaccessible due to its protection level
Which means the InternalsVisibleTo was not picked up.
I was expecting that Roslyn would automatically pick this up from the parsed project info, but I am now wondering if I have to enable some specific compilation option or to add some information manually?
I have looked around but I cannot find a similar question or an answer, unless I am missing something. I can give some more information if required. Thanks!

InternalsVisibleTo requires matching the public key used for signing the other assembly exactly (or no public key in case of unsigned assembly).
Double check you are providing either that correct key and the correct assembly name including the namespace.
Particularly, here, you are interested to the full public key, and not the public key token.
For more information and how to extract the public key for this purpose, please have a look at InternalsVisibleToAttribute Class.

Related

Is there a way to "cap" RoslynPad's Roslyn's IntelliSense?

I'm actually integrating the amazing RoslynPad into a WinForms application and working damn well.
The point of the integration is allowing the user to type in some C# code so it can be used in a future.
Thing is I'm interested on "capping" the user so he could just use some System or even LinQ functions. I don't want to allow the user to think he is allowed to use System.IO and others. Of course I can't prevent him/her typing System.IO.File.Delete, but will surely help if the System.IO's Assembly is not loaded into the RoslynPad's IntelliSense.
The source code typed by the user is going to be compiled locally before being saved into the DB. I'm adding just a few and necessary Assemblies for the compilation, so if System.IO it won't compile, of course.
As I explained, I just want to cap the Intellisense, so they don't think they have access to almost the whole .NET Framework.
EDIT: Added the actual implementation actually done. I'm loading "RoslynPad.Roslyn.Windows" and "RoslynPad.Editor.Windows" assemblies to the editor.
private RoslynCodeEditor _editor;
private void InitializeEditor(string sourceCode)
{
if (string.IsNullOrWhiteSpace(sourceCode))
sourceCode = string.Empty;
_editor = new RoslynCodeEditor();
var workingDirectory = Directory.GetCurrentDirectory();
var roslynHost = new RoslynHost(additionalAssemblies: new[]
{
Assembly.Load("RoslynPad.Roslyn.Windows"),
Assembly.Load("RoslynPad.Editor.Windows")
});
_editor.Initialize(roslynHost, new ClassificationHighlightColors(), workingDirectory, sourceCode);
_editor.FontFamily = new System.Windows.Media.FontFamily("Consolas");
_editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
_editor.FontSize = 12.75f;
elementHost1.Child = _editor;
this.Controls.Add(elementHost1);
}
You can use pass a RoslynHostReferences instance to the RoslynHost constructor, and decide which assemblies and namespaces are imported by default.
You could use the same logic as Default, just remove System.IO.Path from the type list.
Note that System.IO is not an assembly, but rather a namespace, which is in the core library, so there's no simple way to completely remove it.

Get assembly name at compile time and use it in code [duplicate]

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;
}
}

Dynamically compiled project losing resources

I need to compile source code of big project dynamically and output type can be Windows Application or Class Library.
Code is nicely executed and its possible to make .dll or .exe files, but problem is that, when I'm trying to make .exe file - it's losing resources like project icon. Result file doesn't include assembly information to.
Any way to solve this? (Expected result should be the same, that manual Build function on project file in Visual Studio 2015).
Thank you!
var workspace = MSBuildWorkspace.Create();
//Locating project file that is WindowsApplication
var project = workspace.OpenProjectAsync(#"C:\RoslynTestProjectExe\RoslynTestProjectExe.csproj").Result;
var metadataReferences = project.MetadataReferences;
// removing all references
foreach (var reference in metadataReferences)
{
project = project.RemoveMetadataReference(reference);
}
//getting new path of dlls location and adding them to project
var param = CreateParamString(); //my own function that returns list of references
foreach (var par in param)
{
project = project.AddMetadataReference(MetadataReference.CreateFromFile(par));
}
//compiling
var projectCompilation = project.GetCompilationAsync().Result;
using (var stream = new MemoryStream())
{
var result = projectCompilation.Emit(stream);
if (result.Success)
{
/// Getting result
//writing exe file
using (var file = File.Create(Path.Combine(_buildPath, fileName)))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(file);
}
}
}
We never really designed the workspace API to include all the information you need to emit like this; in particular when you're calling Emit there's an EmitOptions you can pass that includes, amongst other things, resource information. But we don't expose that information since this scenario wasn't hugely considered. We've done some of the work in the past to enable this but ultimately never merged it. You might wish to consider filing a bug so we officially have the request somewhere.
So what can you do? I think there's a few options. You might consider not using Roslyn at all but rather modifying the project file and building that with the MSBuild APIs. Unfortunately I don't know what you're ultimately trying to achieve here (it would help if you mentioned it), but there's a lot more than just the compiler invocation that is involved in building a project. Changing references potentially changes other things too.
It'd also be possible, of course, to update MSBuildWorkspace yourself to pass this through. If you were to modify the Roslyn code, you'll see we implement a series of interfaces named "ICscHostObject#" (where # is a number) and we get passed the information from MSBuild to that. It looks like we already stash that in the command line arguments, so you might be able to pass that to our command line parser and get the data back you need that way.

PostSharp Multicast not working in Outlook plugin code

I am using PostSharp in an Outlook Plugin app. If I add the following attribute to a class in my project it logs properly:
namespace Foo.Bar
{
[Log(AttributeTargetMemberAttributes = MulticastAttributes.Public)]
public class FooBar {...}
}
What I really want to do is log everything in the Foo.* namespace. I tried using the addin in VS which created a globalaspects.cs and updated my project.pssln file. At this point it wont build with the following error msg:
.dll uses non-licensed features (PostSharp Professional). Please enter a valid license key.
I figured it was recursing on itself so I added an AttributeExclude = true in the assembly line that was generated for me. It now looks like this (in globalaspects.cs):
[assembly: Log(AttributeExclude = true, AttributeTargetTypes = "Foo.*", AttributeTargetTypeAttributes = MulticastAttributes.Public, AttributeTargetMemberAttributes = MulticastAttributes.Public)]
No luck, it doesn't log anything this way. Any ideas why?
Additional info:
I am logging to log4net and I have other logging code that is working (it also works at both the class and method levels with PostSharp).
According to this page free license of PostSharp currently has limitation on number of methods on which [Log] attribute is applied. In my opinion, you have exceeded this number by applying the aspect on the whole namespace.
AttributeExclude means that the attribute will not be applied on declarations that satisfy conditions set in this attribute. It is basically set inclusion/exclusion operation. For example you can include Namespace1, exclude Namespace1.Namespace2 and again include Namespace1.Namespace2.Namespace3.
Therefore the following would be correct:
[assembly: Log(AttributeTargetTypes = "Foo.*",
AttributeTargetTypeAttributes = MulticastAttributes.Public,
AttributeTargetMemberAttributes = MulticastAttributes.Public)]
For more information about attribute multicasting, you can take a look on this article.
Note to reviewers: I'm one of developers working on PostSharp. I'm aware that this answer touches licensing, which is behind the red line and I have tried my best not to cross it too much.

Including an embedded resource in a compilation made by Roslyn

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.

Categories