i'm currently struggeling with the CodeDOM code generator and executing compiled assemblies.
Everything works like a charm, except running the compiled code a second time.
Settings
The user "programs" a model which will be translated into a executable program. The user can define whether the assembly should created in memory only or on disc, whether to have source code or only an executable. When he clicks the "run" button, the CodeDOM tree is put together and compiled, written out to disc (if needed) and executed.
Exception
When he clicks the "run" button a second time, an exception is thrown:
error CS0016: Unable to write to output file '': --
"The process cannot access the file because it is being used by
another process."
As I can compile the code as often as I want without encountering the error, I would suggest it has something to do how I run the assembly. I searched the web for information on this topic, but all I came up was creating a separate AppDomain and unloading it afterwards.
Here is the snippet which executes the assembly:
if ( RunProject )
{
_log.info( "Compiled without errors, running..." );
Assembly compiledAssembly = res.CompiledAssembly;
AppDomain compiledAssemblyDomain = AppDomain.CreateDomain( "compiledAssemblyDomain" );
compiledAssemblyDomain.ExecuteAssemblyByName( compiledAssembly.GetName( ) );
AppDomain.Unload( compiledAssemblyDomain );
}
The executable file can only be removed if I quit the program, as if the file is locked by the current appdomain. What to do? Thanks for your help!
Update
When the above code executes, the main file is loaded into the executing assembly (or am I wrong?). The debugging console caputes the following information:
[13:42:19.5576171] i Compiled without errors, running...
'XXX.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\...\bin\main.exe'
and just a few seconds after quitting the executed assembly:
The thread '.NET SystemEvents' (0x20d0) has exited with code 0 (0x0).
The thread '<No Name>' (0x1d20) has exited with code 0 (0x0).
where XXX is the name of my main application compiling the code. Shouldn't the file loaded somewhere else? Doesn't XXX.vshost.exe open a handle and don't close it after unloading the AppDomain?
I have wrestled with this quite a bit (I did something similar with Scrolling Game Development Kit 2). You have to be very carefully to make sure that everything you do with that compiled code happens within that other AppDomain so that when you unload that AppDomain, all references to the DLL are unloaded with it. If you so much as refer to a type from the compiled code, that DLL will get loaded into your AppDomain as well and unloading the other domain will do no good. So what I have had to do was define interfaces in a common DLL which can get loaded into both domains so that I can call functions in the other DLL without loading types from the other DLL. Just make sure that every object you instantiate in the other DLL uses an interface defined in the shared DLL (or another public interface not defined in the user-defined DLL). Then cast each object you instantiate from that DLL to one of those interfaces. You can never use the types defined in that DLL directly.
EDIT: Observe the following note from MSDN documentation about the CompiledAssembly Property
Note
The get accessor for the CompiledAssembly property calls the Load method to load the
compiled assembly into the current application domain. After calling the get accessor,
the compiled assembly cannot be deleted until the current AppDomain is unloaded.
Related
I have a console application in netcoreapp3.1 that use a netstandard2.0 plugin.
The plugin reference a class library and implement an interface
All dll dependencies are in the the plugin folder and the plugin.dep.json include all referenced library.
When I run:
AssemblyLoadContext.Default.LoadFromAssemblyPath("path/to/main_myplugin.dll");//load plugin
it resolve the type of interface
When i try to run an instance as given below it fail:
if (type != null) //type is resolved and not null
{
var instance = (IContract)Activator.CreateInstance(type); //instance is created
Console.WriteLine($"Create instance : {instance.GetType()}"); // ok instance is created
var ret = instance.Execute(); //!!!fire exception here
Console.WriteLine(ret);
}
and fire error message:
System.IO.FileNotFoundException: 'Could not load file or assembly 'MyLibObjectsLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.'
If I loaded all dependencies, it works fine.
Should I load all dependencies when using AssemblyLoadContext.Default or it's a bug?
I asked this question in the dotnet project
All credit go to #vitek-karas.
The detailed answer is:
Currently this is by design. LoadFromAssemblyPath as well as any other LoadAssembly-like methods only ever load that assembly, they don't try to load a "plugin". For this case to work you would need the AssemblyDependencyResolver and hook it up to the Default ALC (probably via the Resolving event), and hope that there are no collisions between the host app and the plugin (since they will share all dependencies) and so on. Generally this is why it's better to load plugins into their own ALCs as that creates the necessary isolation and also makes it easy to hook up AssemblyDependencyResolver.
Higher-level answer - in 3.0 we didn't try to provide a "plugin load" API as there were too many open questions in how it should behave (the exact isolation behavior is very tricky to get right so that it would work for most use cases). Instead we provided the necessary building blocks (ALC, ADR, diag improvements, ...) and rely on users to write the cca 20 lines of code to implement the custom ALC. In 5.0 we are improving diagnostics by adding detailed tracing around the entire assembly binding process, that should help debugging issues when loading assemblies in general, but specifically when implementing custom ALCs.
I'm making a plugin loader for Unity game (Subnautica). The loading mechanism itself works fine but I want to load the assemblies into a separate app domain so I can unload them at runtime too.
However, calling AppDomain.CreateDomain(string) crashes Unity, leaving an error log with
Unknown caused an Access Violation (0xc0000005)
in module Unknown at 0033:049f7717.
...
Read from location 00000000 caused an access violation.
and the log the game produces with
0x0000000005048C6D ((<unknown>))
ERROR: SymGetSymFromAddr64, GetLastError: 'The specified module could not be found.'
ERROR: SymGetModuleInfo64, GetLastError: 'A dynamic link library (DLL) initialization routine failed.'
I have tried injecting the code that calls AppDomain.CreateDomain into the game's code directly and I tried injecting code that loads the dll that calls AppDomain.CreateDomain using Assembly.Load, both methods crash with the same message.
Unity version is 5.4.4f1, I'm on Windows 10 and the assembly that calls AppDomain.CreateDomain uses .net 3.5. This is the error.log created by Unity and the stack trace from output_log.txt created by Subnautica.
Does anyone have any idea what could be causing Unity to crash?
PS, I know with certainty that AppDomain.CreateDomain is causing the problem.
I've looked over a few questions with a title just like this one, but they either do not talk about command line, or don't seem to work for me for some reason. From what I have read, it seemed as if I could "simply" do the following:
The dummy code (C#):
using System;
public static class Foo {
public static void Bar() {
Console.WriteLine("o3o");
}
}
More dummy code (Visual C++):
#using <test.dll>
int main() {
Foo::Bar();
return 0;
}
C# DLL compiled using:
csc /out:test.dll /t:library src\cs\Foo.cs
Visual C++ object file compiled using:
cl /Ox /clr /AI. /c src\vc\test.cpp
Executable compiled using:
link /out:test.exe test.obj
The following exception is thrown upon running the executable:
Unhandled Exception: System.TypeLoadException: Could not load type 'Foo' from assembly 'test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
at main()
at mainCRTStartup()
I have a strong feeling that I was supposed to also reference the DLL in the link phase, but I couldn't find any option for linking a DLL similar to GCC's -l option. Attempting to pass the DLL along with the .obj to link causes it to tell me that linking assemblies is not supported. Interestingly, though, if I build a .netmodule instead of a DLL, i.e. by:
csc /out:test.dll /t:module src\cs\Foo.cs
and changing the #using directive to
#using <test.netmodule>
the executable runs without error. This feels a bit weird, for I don't think anybody packages code in .netmodules (what are .netmodules, anyway?).
All of your steps should have worked, but one very simple issue is preventing your program from running.
Namely: Your C# DLL assembly name is test, and your C++/CLI exe assembly has the same name. They both have the same identity.
So when looking for Foo::Bar in the test assembly, the loader first checks if the assembly is loaded in the AppDomain. It is - it's your C++/CLI exe, and you can't have several assemblies with the same identity loaded simultaneously within the same AppDomain. Your C# dll wasn't even given a try.
Just change either one of them and everything will work fine:
link /out:test2.exe test.obj
As for what's a .netmodule, it's the format used for linking managed code statically, that's why you managed to link your C# code with your C++/CLI code without issues. It's roughly the equivalent of a .lib file for managed code.
And you're right, it's not used very often.
I am trying to do some hooking in c# (I'd rather not use Detours or c++) so i have been using EasyHook.
https://easyhook.github.io/
However When i'm doing this
Config.Register( "This description can be anything.", #"SomePathToAnExecutable.exe", "MyInjectionDll.dll");
I get the error:
There was an error while connecting to
target:
System.BadImageFormatException: Unable
to load given assembly
[SomePathToAnExecutable.exe] for
reflection.
Is this a valid NET assembly? --->
System.BadImageFormatException: Could
not load file or assembly
[SomePathToAnExecutable.exe] or one of
its dependencies. The module was
expected to contain an assembly
manifest.
Question 1) Am I right in thinking that SomePathToAnExecutable is the process that you want to hook into???
Question 2) Does the executable have to be managed code then??
I've also asked at on the codeplex project site, but no response.
http://easyhook.codeplex.com/Thread/View.aspx?ThreadId=235616
Answer 1) No. Config.Register registers managed assemblies with the GAC. Thus you register all assemblies participating from your code. This includes the dll you want to inject and the assembly that provides the common interface for the IPCServer. For my it looks like this one for example:
Config.Register("MyHook",
Path.Combine(startupPath, "HookManager.dll"),
Path.Combine(startupPath, "NetworkIncomingHook.dll"),
Path.Combine(startupPath, "NetworkOutgoingHook.dll")
);
The HookManager.dll contains the interface I use to create the IPCServer (and where all messages are send to from the hooked functions). The NetworkIncomingHook.dll and NetworkOutgoingHook.dll are both dlls I inject into my programm. This is done by RemoteHooking.Inject.
2) No. You can hook unmanaged assemblies aswell.
I follow the example from the best answer here to a T, compiling with Pyc.py.
Build Python scripts and call methods from C#
I get an exception at pyScope = pyEngine.ImportModule("MyClass");
no module named MyClass
I believe this to be a bug as sometimes recompilation with Pyc.py will produce a dll ImportModule recognizes, but other times it doesn't.
CONCLUSION: As noted below by digEmAll, compiling modules with Pyc.py to be used in this fashion produces random results. Call clr.CompileModules manually instead.
OK,
I got it.
The module name is the (case sensitive) name of the original .py module, not the compiled dll.
I mean, if your original module name was myClass.py, then you compiled it in MyClass.dll, you must ImportModule("myClass") not ImportModule("MyClass")
EDIT:
the previous code refers to the following compile method:
import clr
clr.CompileModules("CompiledScript.dll", "script.py")
On the contrary, using pyc.py, the generated dll contains a module called __main__ instead of the .py file name.
That's very strange...
IIRC, in python a module call itself __main__ if it's running standalone (i.e. not called by another), but I still don't grasp the connection...