加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

.net – 如何在使用托管C时从其他AppDomain返回托管对象?

发布时间:2020-12-16 06:55:21 所属栏目:百科 来源:网络整理
导读:我正在使用用C编写的非托管库.该库有一个托管的C(CLI)包装器,我正在使用托管代码中的库.非托管库(包括CLI包装器)由第三方编写,但我可以访问源代码. 不幸的是,托管包装器与AppDomains不兼容.非托管库创建线程,并将从这些线程调用托管代码.当托管代码在非默认A
我正在使用用C编写的非托管库.该库有一个托管的C(CLI)包装器,我正在使用托管代码中的库.非托管库(包括CLI包装器)由第三方编写,但我可以访问源代码.

不幸的是,托管包装器与AppDomains不兼容.非托管库创建线程,并将从这些线程调用托管代码.当托管代码在非默认AppDomain中运行时,这会导致问题.我需要跨AppDomain调用才能使用标准工具进行单元测试.

为了解决这个问题,我在托管包装器中引入了委托,并使用Marshal.GetFunctionPointerForDelegate()来获取一个允许跨AppDomain调用成功的函数指针.

这通常很好,但现在我有一个新问题.我有以下一系列事件.

Unmanaged thread ->
Unmanaged code 1 ->
Managed wrapper 1 ->
AppDomain transition (via delegate) ->
Managed wrapper 2 ->
Unmanaged code 2 ->

我遗漏了一些关于库如何允许您覆盖托管代码2上的托管代码中的某些功能的细节,这是从一开始就进行非托管到托管转换的重点.

最终,非托管代码2必须将非托管对象返回到非托管代码1.

如果没有委托和AppDomain,托管包装器2将包装非托管对象并将其返回给托管包装器1,托管包装器1然后将状态转移到非托管代码1使用的非托管对象.

不幸的是,我很难在AppDomain转换中返回托管对象.

我想我必须使通过AppDomain边界传递的托管对象可序列化.但是,这并不容易.相反,我创建了一个简单的类,我可以存储我想要传输的对象的类型和表示对象状态的字符串. Type和String都很容易编组,幸运的是我总是可以使用默认构造函数创建对象的实例,然后从字符串初始化它:

// Message is the base class of large hierarchy of managed classes.

[Serializable]
// Sorry for the "oldsyntax",but that is what the C++ library uses.
__gc class SerializedMessage {
public:
  SerializedMessage(Message* message)
    : _type(message->GetType()),_string(message->ToString()) { }

  Message* Create() {
    Message* message = static_cast<Message*>(Activator::CreateInstance(_type));
    message->InitializeFromString(_string);
    return message;
  }

private:
  Type* _type;
  String* _string;
};

在托管包装器2中,我返回一个SerializedMessage,然后在托管包装器1中,我通过调用SerializedMessage :: Create获取原始消息的副本.或者至少那是我希望实现的目标.

不幸的是,AppDomain转换失败,InvalidCastException的消息无法将类型为’SerializedMessage’的对象强制转换为’SerializedMessage’.

我不确定发生了什么,但错误消息可能表明正在从错误的AppDomain访问SerializedMessage对象.但是,使用Marshal.GetFunctionPointerForDelegate的全部意义是能够跨AppDomains调用.

我还尝试从MarshalByRefObject派生SerializedMessage,但后来得到和InvalidCastException,消息无法将类型为’System.MarshalByRefObject’的对象强制转换为’SerializedMessage’.

当我通过Marshal.GetFunctionPointerForDelegate()返回的指针调用时,我需要做什么才能从另一个AppDomain传回托管对象?

对已接受答案的评论

关于“程序集如何加载到第二个AppDomain”的部分是正确的.

C代码在默认的AppDomain中执行,该AppDomain由单元测试运行器控制.托管程序集将加载到由此单元测试运行程序创建的第二个AppDomain中.我正在使用Marshal.GetFunctionPointerForDelegate来启用从第一个AppDomain到第二个AppDomain的托管C调用.

最初我得到一些FileNotFoundException试图加载我的托管程序集并解决这个问题我将我的托管程序集复制到单元测试运行器的AppBase.我仍然有点困惑为什么.NET坚持加载正在执行的程序集,但后来决定解决这个问题并简单地将丢失的文件复制为kludge.

不幸的是,它在两个不同的AppDomain中从同一个程序集的两个不同副本加载相同的类型,我认为这是InvalidCastException的根本原因.

我的结论是,我无法使用“标准”单元测试运行器来测试托管C库,因为来自此库的回调来自错误的AppDomain,我无法控制.

解决方法

Unable to cast object of type ‘SerializedMessage’ to type ‘SerializedMessage’.

我将重点放在这个问题的核心问题上.真正的消息应该是“类型为Foo.SerializedMessage以键入Bar.SerializedMessage”.换句话说,这里涉及两种类型,来自不同的组件. .NET类型的标识不仅仅是类名,还包括完全限定的程序集名称.哪个是程序集显示名称和程序集版本和文化. DLL地狱反措施.

检查程序集的构建方式,并验证SerializedMessage是否仅在任何程序集中出现一次.它也可能是由程序集加载到第二个AppDomain的方式引起的,使用LoadFile()将导致它例如.它加载没有加载上下文的程序集,并且从这样的程序集加载的任何类型甚至不能与通常加载的完全相同的程序集完全相同.

而且你最好远离GetFunctionPointerForDelegate(),非托管指针不会观察AppDomain边界.虽然我不知道你为什么要使用它.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读