无法从 COM 互操作中使用 AppDomain
我有一段 .NET 代码,由于各种原因(可靠性、部署)必须在单独的 AppDomain 中运行。我创建了一个从 MBR 派生的代理对象,它将调用委托给真实的东西,因此它不会加载到当前的 AppDomain 中。我通过通常的 CreateInstanceAndUnwrap 创建代理。
this.eDirectCommunication = (EDirectCommunicationProxy) this.appDomain.CreateInstanceAndUnwrap(x, y);
当我从 .NET 客户端使用它时,这非常有效,但是当从 COM 客户端加载时,转换会失败。我从透明代理进行投射失败。我验证了所需的类型已在所需的 AppDomain 上创建,并且展开成功,只是转换失败。有趣的是,当两个 AppDomain 具有相同的基目录(这表明程序集绑定失败)时,它确实可以工作。但 Fusion 日志查看器没有提到任何问题。
有两个有点相似的问题这里和这里< /a>,但他们没有提供答案。 任何想法出了什么问题,或者我如何进一步调试它?
I've a piece of .NET code that for various reasons (reliability, deployment) must run in a separate AppDomain. I've created a proxy object deriving from MBR that delegates the calls to the real stuff so it wont load in the current AppDomain. I create the proxy via the usual CreateInstanceAndUnwrap.
this.eDirectCommunication = (EDirectCommunicationProxy) this.appDomain.CreateInstanceAndUnwrap(x, y);
This works great when I use it from a .NET client, however when loaded from a COM client the cast fails. I get a failure to cast from a transparent proxy. I validated that the required type is created on the desired AppDomain and the Unwrap succeeds, just the cast fails. Interestingly, it does work when both AppDomain have the same base directory, which points to assembly binding failures. But the Fusion log viewer doesn't mention any issues.
There are two somewhat similar questions here and here, but they provide no answer.
Any ideas what is going wrong, or how can I debug it further?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
几年前我就遇到了这个问题。 IIRC,问题是调用堆栈跨越两个应用程序域边界,这导致托管对象的代理被封送两次(COM->默认->您的:新对象:->您的->默认)。通常不是问题,但是 .NET COM 封送拆收器 QI 有一个特殊的接口,它表示“嗨,我是一个托管对象,需要特殊的封送行为”(抱歉,不记得 IID - 尝试 ComTrace 或滚动您的自己的 IDispatch impl 并通过 CLR 封送它来查看)。这就是您要处理的地方 - 当它在默认域中运行时,它知道您受到管理,然后请求并尝试加载您的托管类型,只有当您的新域的 basedir 与默认域相同时,这才会成功。这显然违背了整个目的。
我有几种方法来处理这个问题。一种方法是使对象创建异步,这样我就可以将托管代理直接从新域推送到非托管代码(例如,注册非托管回调并直接从新域调用它,绕过默认域)。在您无法端到端控制所有内容的插件场景中,这显然很棘手。
另一个是使用一小段非托管代码来滚动一个愚蠢的代理,当 .NET 封送拆收器询问“您真的是托管对象吗?”时,该代理没有响应。但会不受干扰地通过所有其他 QI 和 IDispatch 上的所有内容(我也将我的 hack 限制为仅 IDispatch,这使得它更容易)。所以新的顺序是:COM->默认->你的:新对象->新代理包装->默认->COM。
主要 PITA - 我当时发现了 CLR 互操作团队的一个人发表的一篇博客文章,该文章在未来的 CLR 版本中提出了一些可能的修复方案,但那是几年前的事了,我不记得是谁了(抱歉,我不再以互操作为生,谢天谢地!)
I ran into this exact problem a few years ago. IIRC, the problem was that the call stack crosses two appdomain boundaries, which causes the proxy to the managed obejct to be marshaled twice (COM->default->yours:new object:->yours->default). Normally not a problem, but there's a special interface that the .NET COM marshaler QIs for that says "hi, I'm a managed object and need special marshaling behavior" (sorry, don't remember the IID- try ComTrace or roll your own IDispatch impl and marshal it through the CLR to see). This is where you're getting hosed- when this runs in the default domain, it knows you're managed then asks for and tries to load your managed type, which will only succeed when your new domain's basedir is the same as the default. This obviously defeats the whole purpose.
There are a couple of ways I've dealt with this. One way is to make the object creation async so I could push the managed proxy out directly to unmanaged code from the new domain (eg, register an unmanaged callback and call it directly from the new domain, bypassing the default domain). This is obviously tricky in an addin scenario where you don't control everything end-to-end.
The other was to have a little slice of unmanaged code that rolled a dumb proxy that DIDN'T respond when the .NET marshaler asked "are you really a managed object?" but would pass through all other QIs and everything on IDispatch unmolested (I limited my hack to IDispatch only, too, which made it much easier). So the new sequence goes: COM->default->yours:new object->new proxy wrapper->default->COM.
Major PITA- I found a blog post back then from a guy on the CLR interop team that was floating some possible fixes for this in a future CLR release, but it was several years ago and I don't remember who it was (sorry, I don't do interop for a living anymore, thank goodness!)