Posted by: Zeeshan Amjad | November 18, 2012

WinRT internals using C++. Part 3


We saw in the first part that there is a different object size when we are using ref keyword when creating class here. We can use one undocumented C++ command line switch “reportAllClassLayout” to see the class structure. We can type “/d1 reportAllClassLayout” at the “Additional Options” text box as shown in the screen shot.

WinRT_Internal_11

And let’s see what output is generated by this command line switch.

class MyClassB    size(24):
    +—
    | +— (base class __IMyClassBPublicNonVirtuals)
    | | +— (base class Object)
0    | | | {vfptr}
    | | +—
    | +—
    | +— (base class Object)
4    | | {vfptr}
    | +—
    | +— (base class IWeakReferenceSource)
    | | +— (base class __abi_IUnknown)
8    | | | {vfptr}
    | | +—
    | | +— (base class Object)
12    | | | {vfptr}
    | | +—
    | +—
16    | __abi_FTMWeakRefData __abi_reference_count
    +—

 

Let’s explore this output. The number at the left most shows the offset of the field. It means that the length of all the fields are 4 bytes except the last one which is 8 bytes as shown by this diagram. In addition to that we can even get the information about vtable just below of it. Here is the information about the vtable of this class.

MyClassB::$vftable@__IMyClassBPublicNonVirtuals@:
    | &MyClassB_meta
    |  0
0    | &MyClassB::__abi_QueryInterface
1    | &MyClassB::__abi_AddRef
2    | &MyClassB::__abi_Release
3    | &MyClassB::__abi_GetIids
4    | &MyClassB::__abi_GetRuntimeClassName
5    | &MyClassB::__abi_GetTrustLevel

MyClassB::$vftable@Object@:
    | -4
0    | &thunk: this-=4; goto MyClassB::__abi_QueryInterface
1    | &thunk: this-=4; goto MyClassB::__abi_AddRef
2    | &thunk: this-=4; goto MyClassB::__abi_Release
3    | &thunk: this-=4; goto MyClassB::__abi_GetIids
4    | &thunk: this-=4; goto MyClassB::__abi_GetRuntimeClassName
5    | &thunk: this-=4; goto MyClassB::__abi_GetTrustLevel

MyClassB::$vftable@__abi_IUnknown@:
    | -8
0    | &thunk: this-=8; goto MyClassB::__abi_QueryInterface
1    | &thunk: this-=8; goto MyClassB::__abi_AddRef
2    | &thunk: this-=8; goto MyClassB::__abi_Release
3    | &thunk: this-=8; goto MyClassB::__abi_Platform_Details_IWeakReferenceSource____abi_GetWeakReference
4    | &thunk: this-=8; goto MyClassB::GetWeakReference

MyClassB::$vftable@Object@IWeakReferenceSource@:
    | -12
0    | &thunk: this-=12; goto MyClassB::__abi_QueryInterface
1    | &thunk: this-=12; goto MyClassB::__abi_AddRef
2    | &thunk: this-=12; goto MyClassB::__abi_Release
3    | &thunk: this-=12; goto MyClassB::__abi_GetIids
4    | &thunk: this-=12; goto MyClassB::__abi_GetRuntimeClassName
5    | &thunk: this-=12; goto MyClassB::__abi_GetTrustLevel

MyClassB::__abi_QueryInterface this adjustor: 0
MyClassB::__abi_AddRef this adjustor: 0
MyClassB::__abi_Release this adjustor: 0
MyClassB::__abi_GetIids this adjustor: 0
MyClassB::__abi_GetRuntimeClassName this adjustor: 0
MyClassB::__abi_GetTrustLevel this adjustor: 0
MyClassB::GetWeakReference this adjustor: 0
MyClassB::__abi_Platform_Details_IWeakReferenceSource____abi_GetWeakReference this adjustor: 0

 

We can construct a following diagram with the help of vtable about the memory layout of the class.

WinRT_Internal_12

__abi_FTMWeakRefData is defined in vccorlib.h header file and it contains two pointers therefore it’s size is 8 bytes. Here is a class diagram of __abi_FTMWeakRefData.

WinRT_Internal_13

Here is a code of __abi_FTMWeakRefData from vccorlib.h

Code Snippet
// A class that represents a volatile refcount, that gets initialized to 0.
class __abi_FTMWeakRefData
{
    __abi_IUnknown* __weakRefSource;
    __abi_IUnknown* __pUnkMarshal;

public:
    __abi_FTMWeakRefData(::Platform::Object^ __targetArg)
    {
        auto __weakRef = ref new ::Platform::Details::WeakReferenceSource(__targetArg);
        __weakRefSource = reinterpret_cast<__abi_IUnknown*>(__weakRef);
        *reinterpret_cast<__abi_IUnknown**>(&__weakRef) = nullptr;

#if !defined(VCWINRT_DLL)
        long __hr = ::__Platform_CoCreateFreeThreadedMarshaler(__targetArg,  reinterpret_cast< ::Platform::Object^*>(&__pUnkMarshal));
#else
        long __hr = ::CoCreateFreeThreadedMarshaler(reinterpret_cast<IUnknown*>(__targetArg),  reinterpret_cast<IUnknown**>(&__pUnkMarshal));
#endif
        if (__hr != 0)
        {
            __weakRefSource->__abi_Release();
            __weakRefSource = nullptr;
        }
        __abi_ThrowIfFailed(__hr);
        
        if (__abi_module != nullptr)
        {
            __abi_module->__abi_IncrementObjectCount();
        }
    }

    __abi_FTMWeakRefData(::Platform::Object^ __targetArg, ::Platform::CallbackContext __contextArg)
    {
        auto __weakRef = ref new ::Platform::Details::WeakReferenceSource(__targetArg);
        __weakRefSource = reinterpret_cast<__abi_IUnknown*>(__weakRef);
        *reinterpret_cast<__abi_IUnknown**>(&__weakRef) = nullptr;

        if (__contextArg == ::Platform::CallbackContext::Any)
        {
#if !defined(VCWINRT_DLL)
            long __hr = ::__Platform_CoCreateFreeThreadedMarshaler(__targetArg,  reinterpret_cast< ::Platform::Object^*>(&__pUnkMarshal));
#else
            long __hr = ::CoCreateFreeThreadedMarshaler(reinterpret_cast<IUnknown*>(__targetArg),  reinterpret_cast<IUnknown**>(&__pUnkMarshal));
#endif
            if (__hr != 0)
            {
                __weakRefSource->__abi_Release();
                __weakRefSource = nullptr;
            }
            __abi_ThrowIfFailed(__hr);
        }

        if (__abi_module != nullptr)
        {
            __abi_module->__abi_IncrementObjectCount();
        }
    }

    // called for a partially created ref class i.e. exception thrown from ctor
    void __abi_dtor()
    {
        if (!__weakRefSource)
        {
            return;
        }

        __weakRefSource->__abi_Release();
        __weakRefSource = nullptr;

        if (__pUnkMarshal)
        {
            __pUnkMarshal->__abi_Release();
            __pUnkMarshal = nullptr;
        }
        
        if (__abi_module != nullptr)
        {
            __abi_module->__abi_DecrementObjectCount();
        }
    }

    inline unsigned long __stdcall Increment() volatile
    {
        if (!__weakRefSource)
        {
            return static_cast<unsigned long>(-1); // Called during destruction
        }

        return reinterpret_cast< ::Platform::Details::WeakReferenceSource^>(__weakRefSource)->IncrementStrongReference();
    }

    inline unsigned long __stdcall Decrement() volatile
    {
        if (!__weakRefSource)
        {
            return static_cast<unsigned long>(-1); // Called during destruction
        }

        unsigned long __refCount = reinterpret_cast< ::Platform::Details::WeakReferenceSource^>(__weakRefSource)->DecrementStrongReference();
        if (__refCount == 0)
        {
            __weakRefSource->__abi_Release();
            __weakRefSource = nullptr;

            if (__pUnkMarshal)
            {
                __pUnkMarshal->__abi_Release();
                __pUnkMarshal = nullptr;
            }

            // When destructing objects at the end of the program, we might be freeing
            // objects across dlls, and the dll this object is in might have already freed its module object.
            if (__abi_module != nullptr)
            {
                __abi_module->__abi_DecrementObjectCount();
            }
        }

        return __refCount;
    }

    inline __abi_IUnknown* GetFreeThreadedMarshaler()
    {
        return __pUnkMarshal;
    }

    inline ::Platform::Details::IWeakReference^ GetWeakReference()
    {
        return reinterpret_cast< ::Platform::Details::IWeakReference^>(__weakRefSource);
    }

    inline long __stdcall Get() volatile
    {
        return reinterpret_cast< ::Platform::Details::WeakReferenceSource^>(__weakRefSource)->GetRefcount();
    }
};

 

Here __abi_IUnknown is similar to IUnknown interface and the reason to define one more interface is defined in comment in vccorelib.h file where define this interface. Here is a code from the vccorlib.h header file.

Code Snippet
//
//// Don't want to define the real IUnknown from unknown.h here. That would means if the user has
//// any broken code that uses it, compile errors will take the form of e.g.:
////     predefined C++ WinRT types (compiler internal)(41) : see declaration of 'IUnknown::QueryInterface'
//// This is not helpful. If they use IUnknown, we still need to point them to the actual unknown.h so
//// that they can see the original definition.
////
//// For WinRT, we'll instead have a parallel COM interface hierarchy for basic interfaces starting with _.
//// The type mismatch is not an issue. COM passes types through GUID / void* combos – the original type
//// doesn't come into play unless the user static_casts an implementation type to one of these, but
//// the WinRT implementation types are hidden.
__interface __declspec(uuid("00000000-0000-0000-C000-000000000046")) __abi_IUnknown
{
public:
    virtual long __stdcall __abi_QueryInterface(::Platform::Guid&, void**) = 0;
    virtual unsigned long __stdcall __abi_AddRef() = 0;
    virtual unsigned long __stdcall __abi_Release() = 0;
};

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: