从应用程序的资源加载 .NET 程序集并从内存运行它,但不终止主/主机应用程序

2024-04-17

介绍


我正在使用共享的下一个 C# 代码示例大卫赫夫南' for 从应用程序的资源加载 .NET 程序集并从内存中运行它 https://stackoverflow.com/a/24033647/1248295:

Assembly a = Assembly.Load(bytes);
MethodInfo method = a.EntryPoint;
if (method != null)
    method.Invoke(a.CreateInstance(method.Name), null);

这里我只是分享一个我也在使用的VB.NET的改编:

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


问题是,如果执行的程序集有应用程序退出指令,那么它也会终止我的主/主机应用程序。例如:

控制台应用程序1.exe从此源代码编译:

Module Module1
    Sub Main()
        Environment.Exit(0)
    End Sub
End Module

当我添加控制台应用程序1.exe到应用程序资源,然后我加载它并使用Assembly.Load方法论,它也会终止我的应用程序,因为调用Environment.Exit.

QUESTION


如何在不修改执行程序集的源代码的情况下防止这种情况发生?

也许我可以做一些事情,比如将一种退出事件处理程序关联到执行的程序集以正确处理/忽略它?此时我有什么选择?

PS:对我来说,无论给定的解决方案是用 C# 还是 VB.NET 编写的。

请注意两件事,第一是我的意图是以自动化/抽象的方式解决这个问题,我的意思是最终结果应该只需要调用传递资源和参数的“执行”方法,而不用担心其余的部分;其次,我希望执行的程序集同步运行,而不是异步运行...以防万一这对于可能的解决方案很重要。


Update:我的第一个解决方案不适用于像OP所要求的程序资源中包含的程序集;相反,它从磁盘加载它。从字节数组加载的解决方案将随之而来(正在进行中)。请注意,以下几点适用于这两种解决方案:

  • As the Environment.Exit()方法由于缺乏权限而抛出异常,该方法的执行将not遇到后继续。

  • 您将需要 Main 方法所需的所有权限,但只需在智能感知中键入“权限”或检查SecurityException's TargetSite属性(这是一个实例MethodBase并会告诉您哪种方法失败了)。

  • 如果您的 Main 中的另一个方法需要UnmanagedCode许可,你运气不好,至少使用这个解决方案。

  • 请注意,我发现UnmanagedCode许可是Environment.Exit() needs 纯粹通过尝试和错误.

解决方案 1:当程序集位于磁盘上时

好的,这就是我到目前为止发现的内容,请耐心等待。我们将创建一个沙盒 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:当程序集是字节数组时

警告:癌症解决方案如下。

当更改我的解决方案以加载字节数组时,OP和我发现了一个奇怪的异常文件未找到异常:即使你传入一个字节数组Assembly.Load(), domain.ExecuteAssemblyByName()由于某些奇怪的原因,仍然在磁盘上搜索程序集。显然我们并不是唯一遇到这个问题的人:加载字节数组汇编 https://stackoverflow.com/questions/17836159/loading-byte-array-assembly.

首先,我们有一个Helper class:

public class Helper : MarshalByRefObject
{
    public void LoadAssembly(Byte[] data)
    {
        var a = Assembly.Load(data);
        a.EntryPoint.Invoke(null, null);
    }
}

正如你所看到的,使用加载程序集Assembly.Load()并调用它的入口点。这是我们将加载到的代码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查看哪个方法引发了异常。

结论/注意事项

这个解决方案并不完美。最好以某种方式检查方法的 IL 并人为地删除对Environment.Exit().

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从应用程序的资源加载 .NET 程序集并从内存运行它,但不终止主/主机应用程序 的相关文章

随机推荐