Run Mono.Cecil in .NET Core - c#

I ran the HelloWorld console app example from a SO anwser compiled with .NET Core 2 and Mono.Cecil 0.10.0-beta7:
var myHelloWorldApp = AssemblyDefinition.CreateAssembly(
new AssemblyNameDefinition("HelloWorld", new Version(1, 0, 0, 0)), "HelloWorld", ModuleKind.Console);
var module = myHelloWorldApp.MainModule;
// create the program type and add it to the module
var programType = new TypeDefinition("HelloWorld", "Program",
Mono.Cecil.TypeAttributes.Class | Mono.Cecil.TypeAttributes.Public, module.TypeSystem.Object);
module.Types.Add(programType);
// add an empty constructor
var ctor = new MethodDefinition(".ctor", Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.HideBySig
| Mono.Cecil.MethodAttributes.SpecialName | Mono.Cecil.MethodAttributes.RTSpecialName, module.TypeSystem.Void);
// create the constructor's method body
var il = ctor.Body.GetILProcessor();
il.Append(il.Create(OpCodes.Ldarg_0));
// call the base constructor
il.Append(il.Create(OpCodes.Call, module.Import(typeof(object).GetConstructor(Array.Empty<Type>()))));
il.Append(il.Create(OpCodes.Nop));
il.Append(il.Create(OpCodes.Ret));
programType.Methods.Add(ctor);
// define the 'Main' method and add it to 'Program'
var mainMethod = new MethodDefinition("Main",
Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.Static, module.TypeSystem.Void);
programType.Methods.Add(mainMethod);
// add the 'args' parameter
var argsParameter = new ParameterDefinition("args",
Mono.Cecil.ParameterAttributes.None, module.Import(typeof(string[])));
mainMethod.Parameters.Add(argsParameter);
// create the method body
il = mainMethod.Body.GetILProcessor();
il.Append(il.Create(OpCodes.Nop));
il.Append(il.Create(OpCodes.Ldstr, "Hello World"));
var writeLineMethod = il.Create(OpCodes.Call,
module.Import(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) })));
// call the method
il.Append(writeLineMethod);
il.Append(il.Create(OpCodes.Nop));
il.Append(il.Create(OpCodes.Ret));
// set the entry point and save the module
myHelloWorldApp.EntryPoint = mainMethod;
myHelloWorldApp.Write("HelloWorld.exe");
The above code executes fine when compiling with .NET Framework, but when compiling with .NET Core resulted in the error:
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Console, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
at HelloWorld.Program.Main(String[] args)
I'd like to ask, why can't the HelloWorld app locate the assembly file mscorlib.dll? What should I do to fix it?

I solved this by creating a HelloWorld.runtimeconfig.json at the same folder of HelloWorld.exe and then ran dotnet ./HelloWorld.exe. It would print Hello World to the console.
The Json file:
{
"runtimeOptions": {
"tfm": "netcoreapp2.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "2.0.0"
}
}
}

you must be performing the assembly manipulation from within .net classic, ie .net 4.x. so this means when u do typeof(Console) is imports the Console from the full runtime, no from the NET Core runtime

If you use typeof(...), you are importing types from the current runtime (.NET Core 2). If you want to make a .NET Framework application from .NET Core, you need to import mscorlib from .NET Framework on Windows, or mono, and use the types from it. See my other answer: https://stackoverflow.com/a/73434552/4205390

Related

FileNotFoundException when loading assembly of ASP.NET Core MVC 5 project

I'm trying to explore an assembly that belongs to an ASP.NET Core MVC project. The project was made with .NET 5.
The problem is, when I try to explore the assembly's types, it throws a FileNotFoundException with the following message: Could not load file or assembly 'Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. El sistema no puede encontrar el archivo especificado.
Issuing a dotnet build command compiles the project without problems.
Exploring the corresponding .csproj file, I found that the Microsoft.AspNetCore.Mvc.Core dependency is not declared in the project file, but the SDK indicated in the project file's XML root is Microsoft.NET.Sdk.Web, and I understand that marks the project as an ASP.NET Core project, but I can't find a way to bring the necessary dependencies to load the assembly.
Code as follows.
Code that loads the assembly:
var reader_context = new ReaderLoadContext(artifact_dir);
var assembly_path = ""; //Path of the compiled assembly
var assembly = reader_context.LoadFromAssemblyPath(assembly_path);
//Here's where it crashes
var target_types = assembly.ExportedTypes.Where(t => t.BaseType != null && t.BaseType.Name == "ControllerBase").ToArray();
ReaderLoadContext's code:
public class ReaderLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public ReaderLoadContext(string readerLocation)
{
_resolver = new AssemblyDependencyResolver(readerLocation);
}
protected override Assembly Load(AssemblyName assemblyName)
{
var assembly_path = _resolver.ResolveAssemblyToPath(assemblyName);
if (assembly_path != null)
{
return LoadFromAssemblyPath(assembly_path);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
var library_path = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (library_path != null)
{
return LoadUnmanagedDllFromPath(library_path);
}
return IntPtr.Zero;
}
}
Turns out this is a known limitation of AssemblyLoadContext and one of the solutions (more like an ugly workaround) is to add a reference to the missing framework in the application that explores the desired assembly. I added a PackageReference item in my app's .csproj file and now it properly loads the assembly I want to scan.
I don't know if I should mark this as an answer. In one hand, it does what I need it to. OTOH, it forces the developer to add all possible framework references beforehand just in case, and if Microsoft (or a 3rd party) releases another framework, it requires releasing a new version of the app.
So hopefully someone knows a more elegant solution.
Thanks to #deepak-msft and everyone else for the help.

CefSharp NuGet package, AnyCPU cannot find files

I am currently trying to write a small service, which uses CefSharp (v57.0.0) to render HTML to a PDF file and followed the instructions to use "Any CPU" in my project (Feature Request - Add AnyCPU Support).
In my project I used the following assembly resolver that seems to work fine (it loads CefSharp.Core.dll, CefSharp.dll during initialisation):
// Will attempt to load missing assembly from either x86 or x64 subdir
private static Assembly Resolver(object sender, ResolveEventArgs args)
{
if (args.Name.StartsWith("CefSharp", StringComparison.Ordinal))
{
string assemblyName = args.Name.Split(new[] { ',' }, 2)[0] + ".dll";
string archSpecificPath = Path.Combine(
AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
Environment.Is64BitProcess ? "x64" : "x86",
assemblyName);
var outputAssembly = File.Exists(archSpecificPath) ? Assembly.LoadFile(archSpecificPath) : null;
return outputAssembly;
}
return null;
}
For the initialisation of CefSharp I set exactly the same values like in the example:
var settings = new CefSettings()
{
// By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache")
};
// Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
However, if I start my simple test, I get the following error code:
Message: System.Exception : Unable to locate required Cef/CefSharp dependencies:
Missing:CefSharp.BrowserSubprocess.exe
Missing:CefSharp.BrowserSubprocess.Core.dll
Missing:CefSharp.Core.dll
Missing:CefSharp.dll
Missing:icudtl.dat
Missing:libcef.dll
Executing Assembly Path:D:\projects\CefService\bin\Debug\x86
Any ideas what might be happening here and how to solve the problem?
The message is pretty clear, other assemblies could not be loaded.
Here are some generic instructions on how to do that:
load native ones (e.g. libcef.dll) first with LoadLibrary and FreeLibrary
see if loading a managed one will automatically load other managed ones it depends, else handle them (tedious)
You might be interested in these tools for spotting dependencies:
http://www.dependencywalker.com/
https://github.com/isindicic/DependencyWalker.Net
How do I determine the dependencies of a .NET application?

Cake (C# make) use .Net Framework methods in build.cake

Maybe a dump question. Cake states that its a build automation system that can be written in C#. I'm actually playing around a bit and now want to know if it is possible to call .Net methods in build.cake. At the time I've the following build.cake:
var target = Argument("target", "Default");
Task("NuGet")
.Does(() =>
{
// Get local directory
// Get all packages.config files in local directory
// Call nuget restore for every file
var currentDir = System.IO.GetCurrentDirectory(); // This doesn't work
var allPgkConfigs = System.IO.Directory.GetFiles(currentDir, "packages.config", System.IO.SearchOption.AllDirectories); // This doesn't work
foreach (var pgk in allPgkConfigs)
{
// GetNuGetPackageId(pkg);
}
});
Task("Build")
.Does(() =>
{
MSBuild("MySolution.sln");
});
RunTarget(target);
When calling build.ps1 -target nuget I get the following error:
PS C:\> .\build.ps1 -Target nuget
Preparing to run build script...
Running build script...
Analyzing build script...
Processing build script...
Compiling build script...
Error: C:/Users/Mewald-T550/XAP_Playground/build.cake(6,26): error CS0234: The type or namespace name 'GetCurrentDirectory' does not exist in the namespace 'System.IO' (are you missing an assembly reference?)
As cake already states it can't find System.IO how can I add this reference to cake?
I know that cake offers some build-in file operations, but I want to know how to add .Net Framework methods to the cake script.
Thx
You're calling a method on a namespace
Change
System.IO.GetCurrentDirectory()
to
System.IO.Directory.GetCurrentDirectory()
Tried this script and it worked just fine
var directory = System.IO.Directory.GetCurrentDirectory();
Information(directory);
That said, Cake has several IO abstractions built in
I.e. this will achieve the same:
var allPgkConfigs = GetFiles("./**/packages.config");
foreach (var pgk in allPgkConfigs)
{
// GetNuGetPackageId(pkg);
}
If you just want the current directory you can use
Context.Environment.WorkingDirectory
or just
var curDir = MakeAbsolute(Directory("./"));
Information("Current directory is: {0}", curDir);
You can use a reference directive:
#r "bin/myassembly.dll"
or
#reference "bin/myassembly.dll"
See http://cakebuild.net/docs/fundamentals/preprocessor-directives
Reference directive
The reference directive is used to reference external assemblies for use in your scripts.
Usage
The directive has one parameter which is the path to the dll to load.
#r "bin/myassembly.dll"
or
#reference "bin/myassembly.dll"
Try make it point to C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.IO.dll

Execute assembly generated at Runtime from Xamarin.iOS

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.

RProvider and Deedle require different FSharp.Core version

I want to write a F# Library that is used in a C# Application.
The purpose is F# library get an input Deedle Frame and use an R script to calculate and return an output Deedle Frame. So in my Visual Studio 2013 solution, there are two projects: one F# library (called RRunner) and one C# console application (which uses the RRunner library). Both of projects use Nuget package: Deedle.RPlugin
type RRunner() =
static member CreateFrameList(input: Frame<int,string>) : Frame<int,string> =
R.assign("input", input) |> ignore
let script = R.source(rScriptPath)
let result: Frame<int, string> = script.GetValue()
result
Inside C# code
var result = RRunner.RRunner.CreateFrameList(inputFrame);
The above F# code works well. However, when I change the return type of CreateFrameList method to:
static member CreateFrameList(input: Frame<int,string>) : List<Frame<int,string>> =
R.assign("input", input) |> ignore
let script = R.source(rScriptPath)
let result: List<Frame<int, string>> = script.GetValue()
result
I can build the F# project successfully, but I can not build the C# project.
The error message:
Assembly 'RRunner, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' uses 'FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' which has a higher version than referenced assembly 'FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
What is the problem with my code? And How to solve this problem? or Is there any other way to resolve my purpose to run R script (that receives a Deedle Frame and returns a List of Deedle Frames) in C# code.

Categories