Loading external assemblies at runtime #65
Replies: 13 comments 3 replies
-
Also when I try to build UnrealEngine.Runtime to %MyProject%\Plugins\UnrealCLR\Managed I'm getting "Unloading of assemblies was failed!" without any code modification (I only changed output build path). Or how should I compile the runtime in a right way? Should I modify the runtime code and do Install again? |
Beta Was this translation helpful? Give feedback.
-
What are you trying to achieve with that? There should be only one referenced As for |
Beta Was this translation helpful? Give feedback.
-
I made a runtime code compilation (players can write, compile and execute code in the game). And it works fine. But I cannot use/reference to UnrealEngine.Framework or UnrealEngine.Tests (or some my custom assembly) in my runtime compiled code.. private void TestRuntimeCompilation()
{
var compilation = CSharpCompilation.Create("c" + Guid.NewGuid())
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(
"using System;\n" +
"using UnrealEngine.Framework;\n" +
"using System.Drawing;\n" +
"" +
"public class TestCode " +
"{" +
" public static void InvokeMe()" +
" {" +
" Debug.Log(LogLevel.Display, \"=========Loaded!==========\");" +//remove this line to execute without exception
" }" +
"}"));
var dotnetCoreDirectory = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
compilation = compilation.AddReferences(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location));
compilation = compilation.AddReferences(MetadataReference.CreateFromFile(@"D:\HomeProjects\UE4\MyProject\Managed\Tests\UnrealEngine.Framework.dll")); //TODO: Fix path for ur side!
compilation = compilation.AddReferences(MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "mscorlib.dll")));
compilation = compilation.AddReferences(MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "netstandard.dll")));
compilation = compilation.AddReferences(MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "System.Runtime.dll")));
using (var memoryStream = new MemoryStream())
{
var emitResult = compilation.Emit(memoryStream);
if (emitResult.Success)
{
memoryStream.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(memoryStream.ToArray()); //context.LoadFromStream(memoryStream);
var type = assembly.GetType("TestCode");
Debug.Log(LogLevel.Error, "=========Executing...==========");
type.GetMethod("InvokeMe", BindingFlags.Public | BindingFlags.Static).Invoke(null, new object[] { });
Debug.Log(LogLevel.Error, "=========Executing done==========");
}
else
{
var failures = emitResult.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
throw new Exception(string.Join(",", failures.Select(x => $"{x.Id}: {x.GetMessage()}")));
}
}
}
|
Beta Was this translation helpful? Give feedback.
-
If you remove this line it will execute without exception: And to be able to run my code above you need to copy two dll's: (you can get them here or copy from GAC) |
Beta Was this translation helpful? Give feedback.
-
We can check that required assembly is actually loaded in memory (obviously, in other case nothing works then). It just cannot resolve it in memory.. Log: Loaded assembly: System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e Log: =========Executing...========== |
Beta Was this translation helpful? Give feedback.
-
Does this line persist in your external assembly which you are trying to load dynamically at runtime? Is that correct? |
Beta Was this translation helpful? Give feedback.
-
Yes, it's in a 14 line of |
Beta Was this translation helpful? Give feedback.
-
You can reference dll to But you cannot do anything with reflection/runtime loading:
dll code: using UnrealEngine.Framework;
namespace zTestApi
{
public class TestCode
{
public static void InvokeMe()
{
Debug.Log(LogLevel.Display, "=========Loaded!==========");
}
}
} LogUnrealCLR: Error: UnrealCLR::Module::Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. |
Beta Was this translation helpful? Give feedback.
-
As I mentioned previously, |
Beta Was this translation helpful? Give feedback.
-
In other words, you can't use framework API from assemblies that loaded dynamically from user code, that's why you get the same exception with |
Beta Was this translation helpful? Give feedback.
-
You can try something like Harmony for that. It might work in this case. |
Beta Was this translation helpful? Give feedback.
-
I see, I got it. Thanks for a help)
Thanks, I'll check again, but as far as I know it is used for patching the methods IL code.. Btw I was able to make everything work (also with
In worst case if I couldn't deal with Harmony I could make a wrapper/separate loader or assemblyLoadContext and just reload dlls like this in case they changed.. UPD. Runtime compilation in memory also works fine! (I'm using a PluginLoader.LoadContext to load the assemblies.. just for a test..) |
Beta Was this translation helpful? Give feedback.
-
We can expose an interface for external plugins, if you have any idea how to do that properly, I'm open to any suggestions. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Thanks for making this great project :)
I got a problem with loading dll's from files in runtime (+using reflection on them later).
They seems loaded fine until they have a reference to UnrealEngine.Framework or UnrealEngine.Tests.
Loading using:
AssemblyLoadContext.Default.LoadFromAssemblyPath(fileName);
AssemblyLoadContext.Default.LoadFromStream(memoryStream);
Assembly.Load(memoryStream.ToArray());
I was also trying to use assembliesContext from AssembliesContextManager (in Runtime.cs) instead AssemblyLoadContext.Default, but error is the same.
LogUnrealCLR: Error: UnrealCLR::Module::Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.IO.FileNotFoundException: Could not load file or assembly 'UnrealEngine.Framework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. ?? ??????? ????? ????????? ????. File name: 'UnrealEngine.Framework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' at TestApi.TestCode.InvokeMe() --- End of inner exception stack trace --- at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) at UnrealEngine.Tests.RuntimeCodeTest.test1() at UnrealEngine.Tests.RuntimeCodeTest.OnBeginPlay() at UnrealEngine.Tests.Main.OnWorldPostBegin() at UnrealEngine.Runtime.Core.ManagedCommand(Command command) in D:\HomeProjects\UE4\UnrealCLR\Source\Managed\Runtime\Runtime.cs:line 141
I gonna help it to resolve assemblies using this:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
private Assembly? CurrentDomainOnAssemblyResolve(object? sender, ResolveEventArgs args)
{
if (args.Name.StartsWith("UnrealEngine.Tests"))
return typeof(RuntimeCodeTest).Assembly;
if (args.Name.StartsWith("UnrealEngine.Framework"))
return typeof(Debug).Assembly;
return null;
}
but getting just another exception:
LogUnrealCLR: Error: UnrealCLR::Module::Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.IO.FileLoadException: Could not load file or assembly 'UnrealEngine.Framework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Operation is not supported. (0x80131515) File name: 'UnrealEngine.Framework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' ---> System.NotSupportedException: Resolving to a collectible assembly is not supported. at TestApi.TestCode.InvokeMe() --- End of inner exception stack trace --- at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) at UnrealEngine.Tests.RuntimeCodeTest.test1() at UnrealEngine.Tests.RuntimeCodeTest.OnBeginPlay() at UnrealEngine.Tests.Main.OnWorldPostBegin() at UnrealEngine.Runtime.Core.ManagedCommand(Command command) in D:\HomeProjects\UE4\UnrealCLR\Source\Managed\Runtime\Runtime.cs:line 141
Should I use some other way of loading.. like Runtime.cs do for referencedAssemblies ?
Thanks)
Beta Was this translation helpful? Give feedback.
All reactions