C#学习教程:从应用程序资源加载.NET程序集并从内存中运行它,但不终止主/主机应用程序终止主/主机应用程序简介我正在使用DavidHeffernan共享的下一个C#代码示例来加载从应用程序的资源中获取.NET程序集并从内存中运行它:Assemblya=Assembly.Load(bytes);MethodInfo方法=a.EntryPoint;if(method!=null)method.Invoke(a.CreateInstance(method.Name),null);在这里我只是分享我在VB.NET中使用的改编:MethodInfo=ass.EntryPointIf(methodIsNotNothing)ThenDiminstanceAsObject=ass.CreateInstance(method.Name)method.Invoke(instance,parameters)如果(instanceIsNotNothing)AndAlso(instance.GetType().GetInterfaces.Contains(GetType(IDisposable)))ThenDirectCast(instance,IDisposable).Dispose()EndIfinstance=Nothingmethod=Nothingass=NothingElseThrowNewEntryPointNotFoundException("在指定的资源中找不到入口点。你确定它是一个.NET程序集?”)EndIfEndSub问题问题是,如果执行的程序集有应用程序退出指令,那么它也会终止我的主/主机应用程序,例如:ConsoleApplication1.exe从这个来源编译:ModuleModule1SubMain()Environment.Exit(0)EndSub结束模块当我将ConsoleApplication1.exe添加到应用程序资源时,我加载它并使用Assembly.Load方法运行它,它也会因为调用Environment.Exit而终止我的应用程序。问题如何在不修改已执行程序集的源代码的情况下防止这种情况发生?也许我可以做一些事情,比如将一种退出事件处理程序附加到已执行的程序集,以正确处理/忽略它?此时我有什么选择?PS:对我来说,给定的解决方案是用C#还是VB.NET编写的并不重要。请注意两点,首先我的意图是以自动/抽象的方式解决这个问题,我的意思是最终结果应该只需要调用“Execute”方法传递资源和参数而不用担心其余的;其次,我希望执行的程序集同步运行,而不是异步运行……如果这对可能的解决方案很重要的话。更新:我的第一个解决方案不适用于OP等程序资源中包含的程序集;而是从磁盘加载它。随后将提供从字节数组加载的解决方案(正在进行中)。请注意,以下内容适用于两种解决方案:解决方案1:当程序集在磁盘上时好的,这是我目前所发现的,所以请多多包涵。我们将创建一个沙盒AppDomain:AppDomainSetupadSetup=newAppDomainSetup();adSetup.ApplicationBase=AppDomain.CurrentDomain.BaseDirectory;//这是主要可执行文件所在的位置。有关这方面的更多信息,请参阅//https://msdn.microsoft.com/en-us/library/system.appdomainsetup.applicationbase(v=vs.110).aspx#Anchor_1PermissionSetpermission=newPermissionSet中的“备注”(许可状态。无);//AppDomain的权限/它可以做什么permission.AddPermission(newSecurityPermission(SecurityPermissionFlag.AllFlags&~SecurityPermissionFlag.UnmanagedCode));//除了UnmanagedCode之外的所有SecurityPermission标志,这是Environment.Exit()所必需的//但是程序集需要SecurityPermissionFlag.Execution才能运行;//否则你会得到一个异常。permission.AddPermission(newFileIOPermission(PermissionState.Unrestricted));permission.AddPermission(newUIPermission(PermissionState.Unrestricted));//以上两个是针对Console.Writ要运行的eLine(),这是我在Main方法中所拥有的varassembly=Assembly.LoadFile(exePath);//ConsoleApplication1.exe的路径vardomain=AppDomain.CreateDomain("SomeGenericName",null,adSetup,permission,null);//沙盒化AppDomaintry{domain.ExecuteAssemblyByName(assembly.GetName(),newstring[]{});}//当(e.TargetSite==typeof(Environment).GetMethod("Exit")){Console.WriteLine("Triedto运行退出");}catch(SecurityExceptione){//你的方法中的一些其他操作需要SecurityPermissionFlag.UnmanagedCode才能运行,//或者PermissionSet缺少一些其他权限}catch{Console.WriteLine("SomethingelsefailedinConsoleApplication1.exe'smain..”);}解决方案2:当程序集是节数组时警告:白斑解决方案如下当更改我的解决方案以加载字节数组时,OP和我发现了一个奇怪的异常,即未找到文件异常:即使您将字节数组传递给Assembly.Load(),domain.ExecuteAssemblyByName()仍然在搜索磁盘时失败对于集会,出于某种奇怪的原因。显然我们不是唯一遇到问题的人:加载字节数组程序集。首先,我们有一个Helper类:publicclassHelper:MarshalByRefObject{publicvoidLoadAssembly(Byte[]data){vara=Assembly.Load(data);a.EntryPoint.Invoke(null,null);如您所见,使用Assembly.Load()加载程序集并调用其入口点。这是我们将加载到AppDomain的代码:AppDomainSetupadSetup=newAppDomainSetup();adSetup.ApplicationBase=AppDomain.CurrentDomain.BaseDirectory;//这是主要可执行文件所在的位置。有关这方面的更多信息,请参阅//https://msdn.microsoft.com/en-us/library/system.appdomainsetup.applicationbase(v=vs.110).aspx#Anchor_1PermissionSetpermission=newPermissionSet中的“备注”(许可状态。无);//AppDomain的权限/它可以做什么permission.AddPermission(newSecurityPermission(SecurityPermissionFlag.AllFlags&~SecurityPermissionFlag.UnmanagedCode));//除了UnmanagedCode之外的所有SecurityPermission标志,这是Environment.Exit()所必需的//但是程序集需要SecurityPermissionFlag.Execution才能运行;//否则你会得到一个异常。permission.AddPermission(newFileIOPermission(PermissionState.Unrestricted));permission.AddPermission(newUIPermission(PermissionState.Unrestricted));//上面两个是给Console.Wr的iteLine()运行,这是我在Main方法中的内容vardomain=AppDomain.CreateDomain("SomeGenericName",null,adSetup,permission,null);//沙盒化的AppDomain//在新的AppDomain中创建一个Helper实例helper.LoadAssembly(bytes);//字节是内存中的程序集}catch(Exceptione){Console.WriteLine("ConsoleApplication1.exe的主程序中出现其他错误..."+e.Message);请注意,在第二个解决方案中,SecurityException变为TargetInvocationException,其InnerException属性为SecurityException,不幸的是,这意味着您无法使用e.TargetSite查看抛出的异常方法。结论/要记住的事情这个解决方案并不完美。以某种方式通过方法的IL并人为地删除对Environment.Exit()的调用会好得多。所有功劳归功于KirillOsenkov-MSFT我可以成功地将程序集加载到另一个AppDomain并调用它的入口点。Environment.Exit总是关闭宿主进程。作为解决方法,从加载的控制台应用程序的Main返回一个int。零表示成功,零表示其他错误号。而不是这个:ModuleModule1SubMain()//yourcodeEnvironment.Exit(0)EndSubEndModule写:(我希望这是有效的VB.NET:-))ModuleModule1FunctionMain()AsInteger//你的代码Return0//0==noerrorEndFunctionEndModule表演–C#classProgram{staticvoidMain(string[]args){Launcher.Start(@"C:Userspathtoyourconsoleapp.exe");}}publicclassLauncher:MarshalByRefObject{publicstaticvoidStart(stringpathToAssembly){TextWriteroriginalConsoleOutput=Console.Out;StringWriterwriter=newStringWriter();控制台.SetOut(作者);AppDomainappDomain=AppDomain.CreateDomain("加载域");启动器程序=(Launcher)appDomain.CreateInstanceAndUnwrap(typeof(Launcher).Assembly.FullName,typeof(Launcher).FullName);程序.执行(pathToAssembly);AppDomain.Unload(appDomain);Console.SetOut(原ConsoleOutput);字符串结果=writer.ToString();控制台.WriteLine(结果);}//////这会在临时appd中执行哦。///没有错误处理来简化演示。///publicvoidExecute(stringpathToAssembly){//加载字节并使用反射运行Main()//如果程序集不是来自磁盘byte[]bytes=File.ReadAllBytes(pathToAssembly);//“Program.exe”程序集assembly=Assembly.Load(bytes);MethodInfomain=assembly.EntryPoint;main.Invoke(null,newobject[]{null});另请注意:另请注意,如果您使用LoadFrom,您可能会得到FileNotFoundexception,因为程序集解析器将尝试查找您在GAC或当前应用程序的bin文件夹中加载的程序集使用LoadFile加载任意组件文件-但请注意如果这样做,您将需要自己加载任何依赖项。只有一种方法可以做到这一点。您必须动态检测将由程序集执行的所有代码。这归结为拦截系统调用。没有简单的方法可以做到这一点。请注意,这不需要修改源代码。为什么.NET安全系统不能这样做?虽然系统可以为您提供可用于控制对Environment.Exit的调用的安全权限,但这并不能解决问题。程序集仍然可以调用非托管代码。其他答案指出这可以通过创建AppDomain并撤销SecurityPermissionFlag.UnmanagedCode来完成。实际上,这可行,但您在注释中指出您正在使程序集调用非托管代码。这是如果你想在同一个进程中运行代码。您也可以在另一个进程中运行代码,但是您必须进行进程间通信。多一些AppDomain代码可以帮助您找到解决方案。可以在LoadUnload中找到代码LoadUnload项目中包含的小程序包含您可以在解决方案中采用的AppDomain代码。C#教程就这些:从应用程序的资源加载.NET程序集并从内存中运行它而不终止主/主机应用程序分享所有内容,如果它对你们有用并且需要了解更多关于C#教程的信息,我希望大家能够多多关注~本文摘自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
