Gon*_*eur 5 c# dll mono appdomain .net-assembly
我目前正在尝试制作一个游戏引擎,尝试逐个复制统一功能,用于加载一些脚本,我没有问题,但是当我必须重新加载它们时,单声道编译失败,告诉我DLL是已经访问过,或者无法删除DLL文件。
这是我的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics;
using System.Security.Policy;
using System.Security;
using System.Security.Permissions;
public class Proxy : MarshalByRefObject
{
public Assembly GetAssembly(string assemblyPath)
{
try
{
byte[] response = new System.Net.WebClient().DownloadData(assemblyPath);
return Assembly.ReflectionOnlyLoad(response);
}
catch (Exception)
{
return null;
}
}
public Assembly GetAssembly2(string assemblyPath, AppDomain domain)
{
try
{
byte[] bytesDLL = new System.Net.WebClient().DownloadData(assemblyPath);
return domain.Load(bytesDLL);
}
catch (Exception)
{
return null;
// throw new InvalidOperationException(ex);
}
}
public Assembly GetAssemblyByName(AssemblyName name, AppDomain domain)
{
return domain.ReflectionOnlyGetAssemblies().
SingleOrDefault(assembly => assembly.GetName() == name);
}
}
class Program
{
public static AppDomain domain;
public static Assembly assembly;
public static Type type;
public static String dllPath;
public static String scriptPath;
public static String className;
public static String file;
public static dynamic instance;
private static bool Compile(String path, out String dir)
{
ProcessStartInfo start = new ProcessStartInfo();
dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
dir = Path.Combine(dir, Path.GetFileNameWithoutExtension(path) + ".dll");
if (File.Exists(dir))
{
Console.WriteLine("???????");
File.Delete(dir);
Console.WriteLine("???????2");
}
start.FileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Mono\\lib\\mono\\4.5\\mcs.exe");
start.UseShellExecute = false;
start.RedirectStandardError = true;
start.RedirectStandardOutput = true;
start.Arguments = "\"" + path + "\" " + "/target:library" + " " + "/out:" + "\"" + dir + "\""; //+ " " + "/reference:OctogonEngine.dll" + " /reference:AssimpNet.dll";
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardError)
{
string result = reader.ReadToEnd();
Console.WriteLine(result);
}
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.WriteLine(result);
}
}
Console.WriteLine("compilation ok");
return (true);
}
public static void Unload()
{
FileStream[] streams = null;
if (assembly != null)
streams = assembly.GetFiles();
instance = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
type = null;
assembly = null;
AppDomain.Unload(domain);
assembly = null;
if (streams != null)
{
for (int i = 0; i < streams.Length; i++)
{
streams[i].Dispose();
}
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Directory.Delete(cachePath, true);
return;
}
static Assembly GetAssemblyByName(string name, AppDomain domain)
{
return domain.GetAssemblies().
SingleOrDefault(assembly => assembly.GetName().Name == name);
}
public static String cachePath = "./cache/";
public static void Load()
{
Directory.CreateDirectory(cachePath);
if (Compile(scriptPath, out Program.dllPath))
{
if (File.Exists(Program.dllPath))
{
className = Path.GetFileNameWithoutExtension(Program.dllPath);
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.ShadowCopyFiles = "true";
setup.CachePath = cachePath;
domain = AppDomain.CreateDomain(className, null, setup);
domain.DoCallBack(() => AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName("test.dll")));
var assemblyLoader = (Proxy)domain.CreateInstanceAndUnwrap(typeof(Proxy).Assembly.FullName, typeof(Proxy).FullName);
assembly = assemblyLoader.GetAssembly(Program.dllPath);
/*if (assembly == null)
{
Console.WriteLine("damn");
}*/
if (assembly != null)
{
type = assembly.GetType(className);
}
if (File.Exists(scriptPath))
Program.file = File.ReadAllText(scriptPath);
}
}
}
static bool check = false;
static void AppDomainInit(string[] args)
{
if (!File.Exists(args[0]))
{
return;
}
}
public static void init(String scriptPath)
{
if (File.Exists(scriptPath))
{
Program.file = File.ReadAllText(scriptPath);
Program.scriptPath = scriptPath;
Program.Load();
}
}
static void Main(string[] args)
{
Program.init(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "test.cs"));
Program.Unload();
//here is the crash :/
File.Delete(Program.dllPath);
Console.WriteLine("???");
}
}
Run Code Online (Sandbox Code Playgroud)
(为了测试,您可能需要将 mono 复制到执行目录中,可以在以下位置找到: http: //www.mono-project.com/download/)
有人知道我可以做什么,强制删除该 dll 文件,或者使该文件可访问删除吗?
如果没有,有人知道统一加载和重新加载脚本的方式,如何使其成为好方法?
所以我制作了这个对我来说效果很好的例子
项目consoleapplication1.exe
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
try
{
String pathToAssembly = args[0];
AppDomain dom = AppDomain.CreateDomain("some");
AssemblyName assemblyName = new AssemblyName();
assemblyName.CodeBase = "loader.dll";
dom.Load(assemblyName);
object loader = dom.CreateInstanceAndUnwrap("loader", "loader.AsmLoader");
Type loaderType = loader.GetType();
loaderType.GetMethod("LoadAssembly").Invoke(loader, new object[] { pathToAssembly });
//make sure the given assembly is not loaded in the main app domain and thus would be locked
AppDomain.CurrentDomain.GetAssemblies().All(a => { Console.WriteLine(a.FullName); return true; });
AppDomain.Unload(dom);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
File.Delete(pathToAssembly);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
类库loader.dll:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace loader
{
public class AsmLoader: MarshalByRefObject
{
public AsmLoader()
{
}
public void LoadAssembly(string path)
{
AssemblyName n = new AssemblyName();
n.CodeBase = path;
AppDomain.CurrentDomain.Load(n);
}
}
}
Run Code Online (Sandbox Code Playgroud)
类库testasm.dll
... whatever code....
Run Code Online (Sandbox Code Playgroud)
将所有 3 个文件放在同一文件夹中,在该文件夹中打开 cmd 行并发出命令:
consoleapplication1.exe testasm.dll
Run Code Online (Sandbox Code Playgroud)
它将把 loader.dll 加载到主应用程序域中,远程应用程序域从 AsmLoader 对象创建一个编组代理,通过编组的 AsmLoader.LoadAssembly 调用将 testasm.dll 加载到远程域中。然后,consoleapplication1.exe 将当前应用程序域中加载的所有程序集输出到控制台,以查看其中是否未加载 testasm.dll。卸载远程应用程序域,删除 testasm.dll 就好了。
归档时间: |
|
查看次数: |
3066 次 |
最近记录: |