Visar inlägg med etikett out parameter. Visa alla inlägg
Visar inlägg med etikett out parameter. 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.

onsdag 29 juni 2011

Tiplet #1: Extension to make dictionary lookups less clunky

For TValues that are of reference type, looking up values can be made less clunky by the following extension method:

public static TValue TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
        where TValue : class
{
        TValue val;
        dictionary.TryGetValue(key, out val);
        return val;
}

The return value will be null if the key doesn't exist. This will also be the case if there is a key with a null value, but often you don't care about the distinction.

Since the hateful "out" is gone, you can now use the method directly in expressions.

Value val = dictionary.TryGetValue(key);
return dictionary.TryGetValue(key);
other.Foo = replacementFoos.TryGetValue(fooKey) ?? other.Foo;