Why is this dynamic_cast<T> not working?

c++, direct2d

Solution

Standard COM interfaces from Microsoft use `__declspec(novtable)` as part of their definition; if you look at the definition of `DX_DECLARE_INTERFACE` you'll see this is the case here. This means that the base interface classes don't have vtables, only the concrete implementation has one. RTTI doesn't have the necessary information for `dynamic_cast` to work properly.

For COM interfaces you should always use `QueryInterface` to do dynamic casting.

Problem

This should be quite simple. ID2D1LinearGradientBrush derives from ID2D1Brush with a valid vtable. I realize QueryInterface would work here, however my question pertains to dynamic_cast. ``` [definition from d2d1.h] interface DX_DECLARE_INTERFACE("2cd906ab-12e2-11dc-9fed-001143a055f9") ID2D1LinearGradientBrush : public ID2D1Brush { // .... } ``` However, given the over-simplified example functions ... ``` bool ClampToItem(ID2D1Brush *brush, SizeF itemSize) { // As expected, works when a linear gradient brush is the parameter. ID2D1LinearGradientBrush *linearGradientBrush = static_cast<ID2D1LinearGradientBrush *>(brush); linearGradientBrush->SetStartPoint(D2D1_POINT_2F{ itemSize.Width, 0.0f }); linearGradientBrush->SetEndPoint(D2D1_POINT_2F{ itemSize.Width, itemSize.Height }); return true; } bool ClampToItem2(ID2D1Brush *brush, SizeF itemSize) { // this dynamic cast FAILS EVERY TIME with an access violation ID2D1LinearGradientBrush *linearGradientBrush = dynamic_cast<ID2D1LinearGradientBrush *>(brush); if (!linearGradientBrush) // <-- never gets here return false; linearGradientBrush->SetStartPoint(D2D1_POINT_2F{ itemSize.Width, 0.0f }); linearGradientBrush->SetEndPoint(D2D1_POINT_2F{ itemSize.Width, itemSize.Height }); return true; } ``` Since I will not be certain that a ID2D1LinearGradientBrush will be provided as a parameter, I would like to use a dynamic_cast. I must be missing something simple. Do these COM objects not contain RTTI info? Thank you for your help. ``` // For clarification, this works as expected bool ClampToItem3(ID2D1Brush *brush, SizeF itemSize) { ID2D1LinearGradientBrush *linearGradientBrush; HRESULT hr = brush->QueryInterface(__uuidof(ID2D1LinearGradientBrush), (void **)&linearGradientBrush); if (hr == S_OK) { linearGradientBrush->SetStartPoint(D2D1_POINT_2F{ itemSize.Width, 0.0f }); linearGradientBrush->SetEndPoint(D2D1_POINT_2F{ itemSize.Width, itemSize.Height }); linearGradientBrush->Release(); linearGradientBrush = nullptr; } return true; } ``` EDIT: Tracing into the dynamic cast (into rtti.cpp): ``` extern "C" PVOID __CLRCALL_OR_CDECL __RTDynamicCast( PVOID inptr, // Pointer to polymorphic object LONG VfDelta, // Offset of vfptr in object PVOID SrcType, // Static type of object pointed to by inptr PVOID TargetType, // Desired result of cast BOOL isReference) // TRUE if input is reference, FALSE if input is ptr throw(...) ``` is called, inptr is valid, vfDelta is 0, SrcType and TargetType look great, isRef says false. Tracing further, the memory access violation occurs here: ``` // Ptr to CompleteObjectLocator should be stored at vfptr[-1] _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]); char *pCompleteObject = (char *)inptr - COL_OFFSET(*pCompleteLocator); ``` since *pCompleteLocator was moved to an invalid location.

Original source

Related problems