我正在尝试执行以下操作:
以下是我的代码尝试将程序集加载到新的应用程序域:
public object Execute(byte[] agentCode)
{
var app = AppDomain.CreateDomain("MonitoringProxy", AppDomain.CurrentDomain.Evidence, new AppDomainSetup {ApplicationBase = AppDomain.CurrentDomain.BaseDirectory}, new PermissionSet(PermissionState.Unrestricted));
app.AssemblyResolve += AppOnAssemblyResolve;
var assembly = app.Load(agentCode);
代码库在最后一行消失,并带有以下消息:
其他信息:无法加载文件或程序集 'Alertera.AgentProxy,Version = 1.0.0.0,Culture = neutral, PublicKeyToken = null'或其依赖项之一。系统不能 找到指定的文件。
没有代码会遇到AppOnAssemblyResolve函数。 有趣的是它已经正确地读取了组件的名称。此外,Alertera.AgentProxy程序集没有任何外部依赖项,除了在System和Newtonsoft.Json上。但是,Newtsoft.Json已作为资源嵌入其中,因此不需要单独加载。
任何指针?使用.NET 2实现最大兼容性
答案 0 :(得分:2)
也许使用应用域上的回调切换到新创建的应用域的上下文将允许您成功加载?像这样......
public object Execute(byte[] assemblyBytes)
{
AppDomain domainWithAsm = AsmLoad.Execute(assemblyBytes);
....
}
[Serializable]
public class AsmLoad
{
public byte[] AsmData;
public void LoadAsm()
{
Assembly.Load(AsmData);
Console.WriteLine("Loaded into: " + AppDomain.CurrentDomain.FriendlyName);
}
public static AppDomain Execute(byte[] assemblyBytes)
{
AsmLoad asmLoad = new AsmLoad() { AsmData = assemblyBytes };
var app = AppDomain.CreateDomain("MonitoringProxy", AppDomain.CurrentDomain.Evidence, new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory }, new PermissionSet(PermissionState.Unrestricted));
app.DoCallBack(new CrossAppDomainDelegate(asmLoad.LoadAsm));
return app;
}
}
编辑:
这是一个更完整的示例,其中显示了如何加载程序集并将信息传递回调用应用程序域,还卸载了为加载程序集而创建的应用程序域。
class Program
{
static void Main(string[] args)
{
var assemblyBytes = File.ReadAllBytes(@"C:\dev\Newtonsoft.Json.dll");
// load an unload the same assembly 5 times
for (int i = 0; i < 5; i++)
{
var assemblyContainer = AssemblyContainer.LoadAssembly(assemblyBytes, true);
var assemblyName = assemblyContainer.AssemblyName;
assemblyContainer.Unload();
}
Console.ReadKey();
}
}
[Serializable]
public class AssemblyContainer
{
public byte[] AssemblyData { get; set; }
public bool ReflectionOnly { get; set; }
private AppDomain Container { get; set; }
public AssemblyName AssemblyName { get; set; }
/// <summary>
/// Unload the domain containing the assembly
/// </summary>
public void Unload()
{
AppDomain.Unload(Container);
}
/// <summary>
/// Load the assembly
/// </summary>
/// <remarks>This will be executed</remarks>
public void LoadAssembly()
{
var assembly = ReflectionOnly ? Assembly.ReflectionOnlyLoad(AssemblyData) : Assembly.Load(AssemblyData);
AssemblyName = assembly.GetName();
// set data to pick up from the main app domain
Container.SetData("AssemblyData", AssemblyName);
}
/// <summary>
/// Load the assembly into another domain
/// </summary>
/// <param name="assemblyBytes"></param>
/// <param name="reflectionOnly"></param>
/// <returns></returns>
public static AssemblyContainer LoadAssembly(byte[] assemblyBytes, bool reflectionOnly = false)
{
var containerAppDomain = AppDomain.CreateDomain(
"AssemblyContainer",
AppDomain.CurrentDomain.Evidence,
new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
},
new PermissionSet(PermissionState.Unrestricted));
AssemblyContainer assemblyContainer = new AssemblyContainer()
{
AssemblyData = assemblyBytes,
ReflectionOnly = reflectionOnly,
Container = containerAppDomain
};
containerAppDomain.DoCallBack(new CrossAppDomainDelegate(assemblyContainer.LoadAssembly));
// collect data from the other app domain
assemblyContainer.AssemblyName = (AssemblyName)containerAppDomain.GetData("AssemblyData");
return assemblyContainer;
}
}
答案 1 :(得分:0)
我这样做是这样的:
Public Class ApplicationDomainBridge
Inherits MarshalByRefObject
Public ReturnValue As Object
Public ErrorObject As System.Exception
End Class
<Serializable>
Public Class AsmHelper
' Inherits MarshalByRefObject
Private Class ApplicationDomainBridge
Inherits MarshalByRefObject
Public ErrorObject As System.Exception
Public Eval As ReportTester.Parameters.AbstractEvaluator
End Class
Private AsmData As Byte()
Private CallbackResult As ApplicationDomainBridge
Sub New()
Me.CallbackResult = New ApplicationDomainBridge
End Sub
Public Sub LoadAsmAndExecuteEval()
Try
' System.Console.WriteLine("Executing in: " & AppDomain.CurrentDomain.FriendlyName)
' Throw New System.InvalidCastException("Test")
Dim assembly As System.Reflection.Assembly = System.Reflection.Assembly.Load(AsmData)
' Here we do something
' The object you return must have
' Inherits MarshalByRefObject
Dim programType As System.Type = assembly.GetType("RsEval")
Dim eval As ReportTester.Parameters.AbstractEvaluator = DirectCast(System.Activator.CreateInstance(programType), ReportTester.Parameters.AbstractEvaluator)
Me.CallbackResult.Eval = eval
Catch ex As Exception
Me.CallbackResult.ErrorObject = ex
End Try
End Sub
Public Shared Function ExecuteInAppTemporaryAppDomain(temporaryAppDomain As AppDomain, ByVal assemblyBytes As Byte()) As ReportTester.Parameters.AbstractEvaluator
Dim loadExecute As AsmHelper = New AsmHelper() With {
.AsmData = assemblyBytes
}
temporaryAppDomain.DoCallBack(New CrossAppDomainDelegate(AddressOf loadExecute.LoadAsmAndExecuteEval))
loadExecute.AsmData = Nothing
Dim retValue As ReportTester.Parameters.AbstractEvaluator = Nothing
If loadExecute.CallbackResult.ErrorObject Is Nothing Then
retValue = loadExecute.CallbackResult.Eval
loadExecute.CallbackResult = Nothing
loadExecute = Nothing
End If
If loadExecute IsNot Nothing AndAlso loadExecute.CallbackResult IsNot Nothing AndAlso loadExecute.CallbackResult.ErrorObject IsNot Nothing Then
Throw loadExecute.CallbackResult.ErrorObject
End If
Return retValue
End Function
End Class
用法:
Dim assemblyBytes As Byte() = System.IO.File.ReadAllBytes(results.PathToAssembly)
Dim temporaryAppDomain As AppDomain = CreateAppDomain()
Dim evaluator As ReportTester.Parameters.AbstractEvaluator = AsmHelper.ExecuteInAppTemporaryAppDomain(temporaryAppDomain, assemblyBytes)
evaluator.Domain = temporaryAppDomain
如果您认为加载应用程序域是很麻烦的,请等到您必须一次性丢弃应用程序域。
我是这样做的:
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
' TODO: verwalteten Zustand (verwaltete Objekte) entsorgen.
If Me.LoadContext IsNot Nothing Then
Me.LoadContext.Unload()
Me.LoadContext = Nothing
End If
If Me.Stream IsNot Nothing Then
Me.Stream.Dispose()
Me.Stream = Nothing
End If
'If Parameters.m_parameterValues IsNot Nothing Then
' Parameters.m_parameterValues.Clear()
' Parameters.m_parameterValues = Nothing
'End If
If Me.Domain IsNot Nothing Then
' https://stackoverflow.com/questions/7793074/unload-an-appdomain-while-using-idisposable
Dim thread As System.Threading.Thread = New System.Threading.Thread(
Sub(obj As System.AppDomain)
Try
' System.Threading.Thread.Sleep(1000)
System.AppDomain.Unload(obj)
Catch ex As System.Threading.ThreadAbortException
' System.Console.WriteLine(ex.Message)
System.GC.Collect()
System.GC.WaitForPendingFinalizers()
End Try
End Sub
)
thread.IsBackground = True
thread.Start(Me.Domain)
Me.Domain = Nothing
End If
System.GC.Collect()
System.GC.WaitForPendingFinalizers()
End If
' TODO: nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalize() weiter unten überschreiben.
' TODO: grosse Felder auf Null setzen.
End If
disposedValue = True
End Sub
如果您执行with the generic idisposable method from SO,它将失败,因为该操作不可序列化...
请注意,麻烦的根源在于,在新的应用程序域中,Assembly.Load代码无法从旧的应用程序域访问字节数组,而是获取一个空的字节数组,因此具有属性Serializable的类。 ..或分别,如果要在ApplicationDomainBridge中返回程序集,尽管在其他域中加载得很好,但是会收到“程序集丢失”异常(序列化问题?)。