Visar inlägg med etikett WRL. Visa alla inlägg
Visar inlägg med etikett WRL. Visa alla inlägg

torsdag 19 juni 2014

C++/CX gotchas #3: Private interfaces (continued)

As an addendum to the last post, I've realized that there is a lot of sense in the way Microsoft themselves seem to implement "private interfaces". We only need to look to IBuffer to see how, since an IBuffer implementation is required to also implement the (pure COM) interface IBufferByteAccess.

Sadly, this means C++/CX is unsuitable since it does not support mixed WinRT/COM classes. The most straight forward way to use private interfaces is therefore to switch to WRL instead.

Even better, you can consume and author these private interfaces in the .NET projection of WinRT, thanks to the very good COM interop support in the CLR.

All you need to do is to make sure the method order and signatures match, and that the interface declarations have the same IID. Let's see how, using the IBufferByteAccess interface on the C++ side (this is from robuffer.h):

    [uuid(905a0fef-bc53-11df-8c49-001e4fc686da)]
    struct IBufferByteAccess : public IUnknown
    {
        STDMETHOD(Buffer)(_Outptr_result_buffer_(_Inexpressible_("size given by different API")) byte **value) = 0;
    };

The C# counterpart of this declaration can be: 

    [ComImport]
    [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IBufferByteAccess
    {
        void Buffer([Out] out IntPtr value);
    }

You can in fact cast an IBuffer directly to this interface. No unsafe code needed.

Also interesting is that the C# method signatures are somewhat flexible. The following is also valid:

    [ComImport]
    [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IBufferByteAccess
    {
        IntPtr Buffer { get; }
    }

In fact, looking at Microsofts own System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBuffer class in DotPeek shows that they use this form. 

Now, why does this even work? Not sure, but I suspect it's NOT to do with the COM interop functionality being clever - since there IS no metadata beyond the GUID here. No TLB or winmd is involved. Rather, I think it's because such a property getter has the exact same signature as the Buffer method with an [Out] parameter does.

söndag 15 september 2013

C++/CX gotchas #1: Careful in the ctor

I've been working within the relatively new world of Windows Runtime/RT, C++/CX, WRL etc lately. It's a somewhat treacherous sea and the maps are sketchy yet (there's not much to find on Google), so I think I'll publish some tips, tricks and gotchas based on what I've found.

In no particular order, here's point number one:

Don't assume too much in the constructor.

Or rather, "in the initializer list".

One of the benefits of C++/CX is that it hides away much of the COM/WinRT plumbing. Your class gets IUnknown, IInspectable implemented automatically, and you don't normally see the intermediate code that's generated.

In this case, I needed weak references to my objects, and as it turns out, there is something called IWeakReferenceSource which is automatically implemented by all ref classes. From what I can tell, you won't get weak reference support unless your object has that plumbing.

In this case, I had set up a sort of fake aggregated "base class" (due to the Windows Runtime limitations) called Base in order to share code between my concrete classes. Each concrete class, e.g. Foo, had a reference to its Base member, and there was also the need for Base to refer back to the Foo. In the Windows Runtime world of reference counting, that means neither object will get destroyed and just lead to a memory leak (or worse, if they aren't just sitting idle).

This is well known of course, and Base needed a weak reference to Foo:

private ref class Base sealed
{
public:
    Base(Foo^ foo) : 
        m_weakFoo(foo)
    {
    }

private:
    Platform::WeakReference m_weakFoo;
};

public ref class Foo sealed
{
public:
    Foo() : 
        m_base(ref new Base(this))
    {
    }

private:
    Base^ m_base;
};

This failed. From Base's perspective, it just got nullptr back from m_weakFoo.Resolve<Foo>().
Head-scratching time ensued.

Observing it in the debugger showed something interesting. Turns out the initializer list is a bit too early to rely on the ref class to be properly set up. Especially the IWeakReferenceSource related members of the class looked quite uninitialized in the debugger. Oops.

And it seems obvious that order of initialization matters... but I've forgotten enough C++/COM to not remember the rules of thumb. I had just wrongly assumed that C++/CX would hold my hand all the way.

So with that lesson learned, I moved the m_base initialization into the constructor body instead:

  Foo() 
  {
      m_base = ref new Base(this);
  }

...and it worked perfectly. (Still does in fact.)

I now stay away from doing any kind of complex logic in the initializer list, at least on my ref classes. 

Simple value initialization should be fine, and really, anything which is pure C++ should work as well, but I figure its better to be safe than sorry.

Feedback welcome, as always.