问题描述
我正在使用 David Heffernan 共享的下一个C#代码示例,用于加载.NET程序集从应用程序的资源中运行并从内存中运行它:
I'm using the next C# code example shared by David Heffernan' for loading a .NET assembly from the application's resources and run It from memory:
Assembly a = Assembly.Load(bytes);
MethodInfo method = a.EntryPoint;
if (method != null)
method.Invoke(a.CreateInstance(method.Name), null);
在这里,我只是在VB.NET中共享了我正在使用的改编版本:
Here I just share an adaptation in VB.NET that I am using too:
Public Shared Sub Execute(ByVal resource As Byte(), ByVal parameters As Object())
Dim ass As Assembly = Assembly.Load(resource)
Dim method As MethodInfo = ass.EntryPoint
If (method IsNot Nothing) Then
Dim instance As Object = ass.CreateInstance(method.Name)
method.Invoke(instance, parameters)
If (instance IsNot Nothing) AndAlso (instance.GetType().GetInterfaces.Contains(GetType(IDisposable))) Then
DirectCast(instance, IDisposable).Dispose()
End If
instance = Nothing
method = Nothing
ass = Nothing
Else
Throw New EntryPointNotFoundException("Entrypoint not found in the specified resource. Are you sure it is a .NET assembly?")
End If
End Sub
问题
问题在于,如果执行的程序集具有应用程序退出指令,那么它也将终止我的主/宿主应用程序.例如:
PROBLEM
The problem is that if the executed assembly has an application exit instruction, then it also terminates my main/host application too. For example:
ConsoleApplication1.exe 从此源代码编译而成:
ConsoleApplication1.exe compiled from this source-code:
Module Module1
Sub Main()
Environment.Exit(0)
End Sub
End Module
将 ConsoleApplication1.exe 添加到应用程序资源中,然后加载并使用 Assembly.Load
方法运行它时,它也会终止我的应用程序,因为调用 Environment.Exit
.
When I add ConsoleApplication1.exe to the application resources, and then I load it and run it with the Assembly.Load
methodology, it also terminates my application because the call to Environment.Exit
.
如何在不修改已执行程序集的源代码的情况下防止这种情况?
How can I prevent this, without modifying the source code of the executed assembly?.
也许我可以做一些事情,例如将一种退出事件处理程序与已执行的程序集关联以正确处理/忽略它?此时我有什么选择?.
Maybe I could do somehting like associate a kind of exit event handler to the executed assembly to handle/ignore it properly?. What are my options at this point?.
PS:对我而言,无论给定的解决方案是用C#还是VB.NET编写.
PS: For me no matter if the given solution is written in C# or VB.NET.
请注意两件事,第一件事是我打算以自动化/抽象的方式解决此问题,我的意思是最终结果应该只需要调用"Execute"方法来传递资源和参数,而不要不用担心剩下的;其次,我希望已执行的程序集能够同步运行,而不是异步运行,以免可能的解决方案很重要.
Please note two things, the first is that my intention is to resolve this issue in an automated/abstracted way, I mean the final result should just need to call the "Execute" method passing the resource and the arguments and don't worry about the rest; and secondly, I want the executed assembly to be ran synchronuslly, not async... in case of that could matter for a possible solution.
推荐答案
更新:我的第一个解决方案不适用于程序资源中包含的程序集,例如OP所要求的;而是从磁盘加载它.从字节数组加载的解决方案将随之而来(进行中).请注意,以下几点适用于两种解决方案:
Update: My first solution doesn't work for assemblies contained in the resources of a program like OP asked; instead it loads it from the disk. The solution for loading from a byte array will follow (in progress). Note that the following points apply to both solutions:
-
由于缺少权限,
Environment.Exit()
方法引发异常,遇到该方法后, 方法将继续执行.
As the
Environment.Exit()
method throws an exception because of lack of permissions, execution of the method will not continue after it is encountered.
您将需要Main方法所需的所有权限,但是您只需在intellisense中键入"Permission"或通过选中 SecurityException
'即可快速找到这些权限.s TargetSite
属性(它是 MethodBase
的一个实例,将告诉您哪个方法失败了.)
You're going to need all the permissions that your Main method needs, but you can find those quickly by just typing in "Permission" in intellisense, or by checking the SecurityException
's TargetSite
property (which is an instance of MethodBase
and will tell you which method failed).
如果Main中的另一个方法需要 UnmanagedCode
权限,则您很不走运,至少使用此解决方案.
If another method in your Main needs the UnmanagedCode
permission, you're out of luck, at least using this solution.
请注意,我发现 UnmanagedCode
权限是 Environment.Exit()
完全出于反复试验而需要 的权限..
Note that I found that the UnmanagedCode
permission was the one that Environment.Exit()
needs purely by trial and error.
好吧,这是我到目前为止发现的,请忍受.我们将创建一个沙箱化AppDomain:
Okay, here's what I've found so far, bear with me. We're going to create a sandboxed AppDomain:
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
// This is where the main executable resides. For more info on this, see "Remarks" in
// https://msdn.microsoft.com/en-us/library/system.appdomainsetup.applicationbase(v=vs.110).aspx#Anchor_1
PermissionSet permission = new PermissionSet(PermissionState.None);
// Permissions of the AppDomain/what it can do
permission.AddPermission(new SecurityPermission(SecurityPermissionFlag.AllFlags & ~SecurityPermissionFlag.UnmanagedCode));
// All SecurityPermission flags EXCEPT UnmanagedCode, which is required by Environment.Exit()
// BUT the assembly needs SecurityPermissionFlag.Execution to be run;
// otherwise you'll get an exception.
permission.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
permission.AddPermission(new UIPermission(PermissionState.Unrestricted));
// the above two are for Console.WriteLine() to run, which is what I had in the Main method
var assembly = Assembly.LoadFile(exePath); // path to ConsoleApplication1.exe
var domain = AppDomain.CreateDomain("SomeGenericName", null, adSetup, permission, null); // sandboxed AppDomain
try
{
domain.ExecuteAssemblyByName(assembly.GetName(), new string[] { });
}
// The SecurityException is thrown by Environment.Exit() not being able to run
catch (SecurityException e) when (e.TargetSite == typeof(Environment).GetMethod("Exit"))
{
Console.WriteLine("Tried to run Exit");
}
catch (SecurityException e)
{
// Some other action in your method needs SecurityPermissionFlag.UnmanagedCode to run,
// or the PermissionSet is missing some other permission
}
catch
{
Console.WriteLine("Something else failed in ConsoleApplication1.exe's main...");
}
解决方案2:当程序集是字节数组时
警告:随后出现癌性溶液.
Solution 2: When the assembly is a byte array
Warning: cancerous solution follows.
在更改我的解决方案以加载字节数组时,OP和我发现了一个奇怪的未找到异常文件异常:即使您将字节数组传递给 Assembly.Load()
,出于某些奇怪的原因, domain.ExecuteAssemblyByName()
仍在磁盘上搜索程序集.显然,我们并不是唯一出现此问题的人:.
When changing my solution to load a byte array, OP and I discovered a weird exception file not found exception: even if you pass in a byte array to Assembly.Load()
, domain.ExecuteAssemblyByName()
still searches the disk for the assembly, for some weird reason. Apparently we weren't the only ones with the issue: Loading Byte Array Assembly.
首先,我们有一个 Helper
类:
public class Helper : MarshalByRefObject
{
public void LoadAssembly(Byte[] data)
{
var a = Assembly.Load(data);
a.EntryPoint.Invoke(null, null);
}
}
如您所见,
使用 Assembly.Load()
加载程序集并调用它的入口点.这是我们将加载到 AppDomain
中的代码:
which as you can see, loads the assembly using Assembly.Load()
and calls it's entry point. This is the code we'll be loading into the AppDomain
:
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
// This is where the main executable resides. For more info on this, see "Remarks" in
// https://msdn.microsoft.com/en-us/library/system.appdomainsetup.applicationbase(v=vs.110).aspx#Anchor_1
PermissionSet permission = new PermissionSet(PermissionState.None);
// Permissions of the AppDomain/what it can do
permission.AddPermission(new SecurityPermission(SecurityPermissionFlag.AllFlags & ~SecurityPermissionFlag.UnmanagedCode));
// All SecurityPermission flags EXCEPT UnmanagedCode, which is required by Environment.Exit()
// BUT the assembly needs SecurityPermissionFlag.Execution to be run;
// otherwise you'll get an exception.
permission.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
permission.AddPermission(new UIPermission(PermissionState.Unrestricted));
// the above two are for Console.WriteLine() to run, which is what I had in the Main method
var domain = AppDomain.CreateDomain("SomeGenericName", null, adSetup, permission, null); // sandboxed AppDomain
try
{
Helper helper = (Helper)domain.CreateInstanceAndUnwrap(typeof(Helper).Assembly.FullName, typeof(Helper).FullName);
// create an instance of Helper in the new AppDomain
helper.LoadAssembly(bytes); // bytes is the in-memory assembly
}
catch (TargetInvocationException e) when (e.InnerException.GetType() == typeof(SecurityException))
{
Console.WriteLine("some kind of permissions issue here");
}
catch (Exception e)
{
Console.WriteLine("Something else failed in ConsoleApplication1.exe's main... " + e.Message);
}
请注意,在第二种解决方案中, SecurityException
变为 TargetInvocationException
,并且其 InnerException
属性是 SecurityException
.不幸的是,这意味着您不能使用 e.TargetSite
来查看哪个方法引发了异常.
Note that in the second solution, the SecurityException
becomes a TargetInvocationException
with it's InnerException
property being a SecurityException
. Unfortunately, this means that you cannot use e.TargetSite
to see which method threw the exception.
此解决方案并不完美.最好以某种方式遍历方法的IL并人为地删除对 Environment.Exit()
的调用.
This solution isn't perfect. It would be much better to somehow go through the IL of the method and artificially remove the call to Environment.Exit()
.
这篇关于从应用程序的资源中加载.NET程序集并从内存中运行它,但不终止主/宿主应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!