Repository: dotnet/runtime
File: src/coreclr/vm/interoputil.cpp (C++)

Code before:
            SafeVariantInit(&DispParams.rgvarg[i]);
            params[i] = &DispParams.rgvarg[i];
        }
    }

    //
    // Retrieve the IDispatch interface that will be invoked on.
    //

    if (pInvokedMT->IsInterface())
    {
        // COMPAT: We must invoke any methods on this specific IDispatch implementation,
        // as the canonical implementation (returned by QueryInterface) may not
        // expose all members.
        // This can occur when pTarget is a CCW for a .NET object and that object's class
        // has a custom default interface (specified by the System.Runtime.InteropServices.ComDefaultInterfaceAttribute attribute).
        // In that case, the default IDispatch pointer will only resolve members defined on that interface,
        // not all members defined on the class.
        //
        // We will still do the QI here because we want to protect the user from the following scenario:
        // - The user has a COM object that implements one IUnknown interface, which we'll call ICallback.
        // - The user defines a managed interface (represented by pInvokedMT)to represent ICallback
        //   - The user incorrectly marks ICallback as "implements IDispatch and not dual".
        // - The user tries to call a method on ICallback.
        //
        // In this case, pInvokedMT wil represent the managed ICallback definition.
        // The underlying COM object will not implement IDispatch.
        //
        // We cannot verify that the vtable of the COM object returned by pInvokedMT will have the IDispatch methods,
        // but we must use that IDispatch implementation (see above).
        // To catch the simple case above (where there is no IDispatch implementation at all),
        // we do a QI for IDispatch and throw away the result just to make sure we don't try to invoke on an object that doesn't implement IDispatch.
        //
        // If the underlying COM object implements IDispatch but the COM interface represented by pInvokedMT is not dispatch or dual,
        // we will not correctly detect that the user did something wrong and will crash.
        // This is a known issue with no solution.
        // Our check here is best effort to catch the simple case where a user may make a mistake.
        SafeComHolder<IUnknown> pInvokedMTUnknown = ComObject::GetComIPFromRCWThrowing(pTarget, pInvokedMT);

        // QI for IDispatch to catch the simple error case (COM object has no IDispatch but pInvokedMT is specified as a dispatch or dual interface)
        SafeComHolder<IUnknown> pCanonicalDisp;
        hr = SafeQueryInterface(pInvokedMTUnknown, IID_IDispatch, &pCanonicalDisp);
        if (FAILED(hr))
            COMPlusThrow(kTargetException, W("TargetInvocation_TargetDoesNotImplementIDispatch"));

        _ASSERTE(IsDispatchBasedItf(pInvokedMT->GetComInterfaceType()));
        // Extract the IDispatch pointer that is associated with pInvokedMT specifically.
        pDisp = (IDispatch*)pInvokedMTUnknown.Extract();
    }
    else
    {

Review comment (suggestion):
There is a typo in the comment: "pInvokedMT wil" should be "pInvokedMT will".
```suggestion
        // In this case, pInvokedMT will represent the managed ICallback definition.
```

Code after:
            SafeVariantInit(&DispParams.rgvarg[i]);
            params[i] = &DispParams.rgvarg[i];
        }
    }

    //
    // Retrieve the IDispatch interface that will be invoked on.
    //

    if (pInvokedMT->IsInterface())
    {
        // COMPAT: We must invoke any methods on this specific IDispatch implementation,
        // as the canonical implementation (returned by QueryInterface) may not
        // expose all members.
        // This can occur when pTarget is a CCW for a .NET object and that object's class
        // has a custom default interface (specified by the System.Runtime.InteropServices.ComDefaultInterfaceAttribute attribute).
        // In that case, the default IDispatch pointer will only resolve members defined on that interface,
        // not all members defined on the class.
        //
        // We will still do the QI here because we want to protect the user from the following scenario:
        // - The user has a COM object that implements one IUnknown interface, which we'll call ICallback.
        // - The user defines a managed interface (represented by pInvokedMT)to represent ICallback
        //   - The user incorrectly marks ICallback as "implements IDispatch and not dual".
        // - The user tries to call a method on ICallback.
        //
        // In this case, pInvokedMT will represent the managed ICallback definition.
        // The underlying COM object will not implement IDispatch.
        //
        // We cannot verify that the vtable of the COM object returned by pInvokedMT will have the IDispatch methods,
        // but we must use that IDispatch implementation (see above).
        // To catch the simple case above (where there is no IDispatch implementation at all),
        // we do a QI for IDispatch and throw away the result just to make sure we don't try to invoke on an object that doesn't implement IDispatch.
        //
        // If the underlying COM object implements IDispatch but the COM interface represented by pInvokedMT is not dispatch or dual,
        // we will not correctly detect that the user did something wrong and will crash.
        // This is a known issue with no solution.
        // Our check here is best effort to catch the simple case where a user may make a mistake.
        SafeComHolder<IUnknown> pInvokedMTUnknown = ComObject::GetComIPFromRCWThrowing(pTarget, pInvokedMT);

        // QI for IDispatch to catch the simple error case (COM object has no IDispatch but pInvokedMT is specified as a dispatch or dual interface)
        SafeComHolder<IUnknown> pCanonicalDisp;
        hr = SafeQueryInterface(pInvokedMTUnknown, IID_IDispatch, &pCanonicalDisp);
        if (FAILED(hr))
            COMPlusThrow(kTargetException, W("TargetInvocation_TargetDoesNotImplementIDispatch"));

        _ASSERTE(IsDispatchBasedItf(pInvokedMT->GetComInterfaceType()));
        // Extract the IDispatch pointer that is associated with pInvokedMT specifically.
        pDisp = (IDispatch*)pInvokedMTUnknown.Extract();
    }
    else
    {