C#和C++/CX对象之间有什么关系?

5

我有一个WP C++运行时组件,需要由C# WP应用程序使用。

在C++运行时组件中,我有:

public interface class ICallback
    {
    public:
        virtual void DoSomething();
    };

public ref class WindowsPhoneRuntimeComponent sealed
    {
    public:
        WindowsPhoneRuntimeComponent();
        void SetCallback(ICallback ^callback);
        IMap<Platform::String^, Platform::Object^>^ CreateDictionary();

    };

在C#应用程序中,我有一个实现了ICallbackCallbackImp。然后我执行以下操作:

CallbackImp cb = new CallbackImp ();
WindowsPhoneRuntimeComponent com = new WindowsPhoneRuntimeComponent();

// Set callback
com.SetCallback(cb);

// Get dictionary
IDictionary<string, object> dict = com.CreateDictionary();

我有以下问题:
  1. cbcom是托管对象。那么C++/CX对象在哪里?我听说cbcom指向一些C++/CX对象(驻留在本地堆上),是吗?
  2. 如果.NET GC释放了cbcom,那么C++/CX对象如何释放?
  3. 当我将cb传递给运行时组件时,cb属于托管堆还是本地堆?
  4. dict位于何处?谁会释放它?
1个回答

6
没有任何关系。C++/CX是一个纯的非托管语言扩展,旨在使与WinRT类型的互操作变得容易。这些类型实际上是底层的COM类型。语法很像托管的C++/CLI语言,主要是因为它们都旨在解决同样的问题,使与非托管类型的互操作变得容易。
在您的C#代码中也会发生类似的情况。您的C#组件以不受管理的WinRT类型公开托管类型。利用了CLR中内置的语言映射。CLR又利用了现有的内置COM互操作。它并不完全隐形,例如您必须声明您的C#类为sealed,这是COM只支持接口继承而不支持实现继承所带来的限制。还有各种其他小技巧,例如必须使用DateTimeOffset而不是DateTime,这是语言映射仅映射DateTimeOffset的副作用。等等。
所以回答您的问题:
1.这里没有C++/CX对象,它们是COM服务器的实现细节。创建WinRT对象的底层低级API是RoCreateInstance(),与COM CoCreateInstance()函数相同。它使用类工厂来创建对象。对象由服务器拥有,除了普通的COM接口指针外,它根本不会暴露给其他代码。
2.在COM和因此WinRT中,内存通过引用计数来管理。IUnknown::AddRef()增加一个引用,IUnknown::Release()释放一个引用。当最后一个Release调用将计数递减到0时,服务器销毁对象。在您的C++/CX代码中,ref new或对象引用赋值语句自动生成AddRef()调用,编译器在C++/CX引用超出作用域时自动生成Release()。这与您在COM代码中使用的CComPtr和_com_ptr_t包装类完全相同,但区别在于编译器负责处理它,而不是您必须自己创建智能指针。还有一个额外的细节,这会删除CCW持有的托管对象引用,最终允许GC对C#对象进行垃圾回收。
3.cb对象存在于GC堆上。如上所述,COM仅公开接口指针,WinRT完全不知道对象实际上位于何处。类工厂和IUnknown方法隐藏了这个细节。
4.同3。

  1. 当我在C#中调用new WindowsPhoneRuntimeComponent()时,WindowsPhoneRuntimeComponent的构造函数被调用,因此必须有一个C++/CX对象驻留在本地堆中? 此外,在运行时组件项目中使用ref new时,我可以看到新创建的对象的地址,那么“该对象由服务器拥有”是什么意思?
  2. “当您的C++/CX引用超出范围时”是什么意思?我个人认为cb和com位于托管堆中,因此GC将决定何时释放它们,这将导致释放C++/CX对象。
- onmyway133
把你自己的心理模型应用于这个问题是可以的,但并不是非常准确的。你需要了解COM才能理解WinRT类型。祝你好运。 - Hans Passant
根据http://social.msdn.microsoft.com/Forums/windowsapps/en-US/d41b7773-d85e-4d1c-97a1-2c8579da62c2/how-does-memory-allocation-work-with-native-winrt-types-in-managed-code的解释,“WinRT类型是在本地堆上创建的,它们不会被垃圾回收。正如您所说,它们是引用计数的。每当C#或VB的调用者通过GC回收释放一个经过投影的.NET对象时,底层RT对象的引用计数就会减少。当引用计数达到0时,将调用其析构函数(如果存在),并且内存将被释放。” - onmyway133
当然,如果这个类型是WinRT本身实现的,那就可以。但是你问的是关于你自己的类型。 - Hans Passant
1
我的自定义类有什么不同吗?我的 C++/CX 对象不是在本地堆上创建的吗? - onmyway133
我们在这里一直打转,你的代码片段正在创建C#对象。理解对象和接口指针之间的区别非常重要。在C#中也存在这种区别,您无法创建接口类型的实例,只能创建实现该接口的类的实例。其他代码可以使用接口引用而不必了解实际对象的任何信息。 - Hans Passant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接