我有一个用C#.NET编写的Windows桌面应用程序,该应用程序需要编译和运行部分受信任的代码。我像一个插件一样处理:将代码编译为.DLL,然后加载程序集并在AppDomain中执行。 AppDomain受到严格限制,仅具有“执行”,“基础结构”和“ RemotingConfiguration”权限,并且具有从DLL所在目录中读取文件的功能。
在调试器中运行时(Visual Studio Community 2017),远程端有时无法续订其租约。然后,对远程“插件管理器”对象进行GC处理,并且主应用程序下一次与插件交互的尝试失败,并显示RemotingException
。
该行为非常不一致。在大多数情况下,一切正常,但是在某些情况下,它会失败。它通常在首次续约之前就失败了,但有时在几次续约后就失败了。我现在唯一的线索是打印到调试器控制台的消息:
Exception thrown: 'System.Runtime.Remoting.RemotingException' in mscorlib.dll
Exception thrown: 'System.Runtime.Remoting.RemotingException' in mscorlib.dll
出现这些消息后的某个时间,“插件管理器”对象被GC(我添加了一个析构函数,该析构函数将消息写入控制台)。因此,似乎遥控器尝试续订租约失败,并且正在收集对象。
为什么会这样?我该怎么做才能防止这种情况?
我在发起人对象中有日志消息,显示主应用程序续订租约。他们看起来像这样:
15:17:20|Lease renewal for PluginCommon.PluginManager, last renewed 00:05:00.1248814 sec ago; renewing for 00:02:00 (host id=1)
15:19:20|Lease renewal for PluginCommon.PluginManager, last renewed 00:02:00.0281627 sec ago; renewing for 00:02:00 (host id=1)
这是我的应用空闲时的预期行为-最初5分钟,每2分钟更新一次。最初的5分钟延迟使调试工作有些沮丧。
请注意,如果没有允许安全性,我将无法覆盖InitializeLifetimeService()
,因此,我不能仅仅使对象永久存在(这在这里是可以接受的解决方案)。我已经尝试了一些具有允许的安全性和10秒超时的实验,但是还没有看到它失败(鉴于失败的偶发性,这不一定意味着什么)。
我的赞助商课程符合您的期望:
class Sponsor<T> : MarshalByRefObject, ISponsor, IDisposable where T : MarshalByRefObject
“插件” AppDomain的创建如下:
PluginManager pm = (PluginManager)mAppDomain.CreateInstanceAndUnwrap(
typeof(PluginManager).Assembly.FullName,
typeof(PluginManager).FullName);
// Wrap it so it doesn't disappear on us.
mPluginManager = new Sponsor<PluginManager>(pm);
赞助的PluginManager对象是主AppDomain与插件AppDomain之间的唯一链接。
在键入此内容时,该应用崩溃了,没有创建租约。我已经将RemotingException添加到异常中断列表中,从而产生了此堆栈跟踪:
mscorlib.dll!System.Runtime.Remoting.Channels.ChannelServices.CheckDisconnectedOrCreateWellKnownObject(System.Runtime.Remoting.Messaging.IMessage msg) Unknown
mscorlib.dll!System.Runtime.Remoting.Channels.ChannelServices.SyncDispatchMessage(System.Runtime.Remoting.Messaging.IMessage msg) Unknown
mscorlib.dll!System.Runtime.Remoting.Channels.CrossAppDomainSink.DoDispatch(byte[] reqStmBuff, System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage smuggledMcm, out System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage smuggledMrm) Unknown
mscorlib.dll!System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatchCallback(object[] args) Unknown
mscorlib.dll!System.Threading.Thread.CompleteCrossContextCallback(System.Threading.InternalCrossContextDelegate ftnToCall, object[] args) Unknown
[AppDomain (Plugin Domain, #2) -> AppDomain (SourceGen.exe, #1)]
mscorlib.dll!System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatch(byte[] reqStmBuff, System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage smuggledMcm, out System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage smuggledMrm) Unknown
mscorlib.dll!System.Runtime.Remoting.Channels.CrossAppDomainSink.SyncProcessMessage(System.Runtime.Remoting.Messaging.IMessage reqMsg) Unknown
mscorlib.dll!System.Runtime.Remoting.Channels.ADAsyncWorkItem.FinishAsyncWork(object stateIgnored) Unknown
mscorlib.dll!System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(object state) Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unknown
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Unknown
[Native to Managed Transition]
我不确定可用的解决方法是什么-也许每60秒进行一次System.Timers.Timer
ping操作,即使插件域无法联系主应用程序也能使事情继续存在吗?
SO上也有类似的问题(例如this),但它们描述了一致的可再现行为。
更新:FWIW,60秒的ping kluge似乎可以正常工作。通话续订机制可以使对象保持预热状态,因此它无需请求续约。