سؤال

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.

هل كانت مفيدة؟

المحلول

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top