Search code examples
visual-c++mfccode-analysisvariant

How can I pass a std::variant as a VARIANT* to ExecWB?


I have looked at this article about using std::variant. This is because the following code was raising a code analysis warning:

void CChristianLifeMinistryHtmlView::OnTimer(UINT_PTR nIDEvent)
{
    if (nIDEvent == ID_TIMER_ZOOM)
    {
        //get the zoom value
        VARIANT vZoom{};
        vZoom.vt = VT_I4;
        vZoom.lVal = 0;
        ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, nullptr, &vZoom);
        TRACE("zoom %d\n", vZoom.lVal);

        //kill the timer
        KillTimer(nIDEvent);

        GetParent()->PostMessage(UWM_HTMLVIEW_CHANGE_ZOOM_MSG, vZoom.lVal);
        return;
    }

    CHtmlView::OnTimer(nIDEvent);
}

The warning:

Warning C26476: Expression/symbol {{0, 0, 0, 0, {0}}} uses a naked union 'union ' with multiple type pointers: Use variant instead (type.7).

I started to try and change the code:

void CChristianLifeMinistryHtmlView::OnTimer(UINT_PTR nIDEvent)
{
    if (nIDEvent == ID_TIMER_ZOOM)
    {
        //get the zoom value
        std::variant<long> vZoom(0);

        ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, nullptr, &vZoom);
        TRACE("zoom %d\n", vZoom.lVal);

        //kill the timer
        KillTimer(nIDEvent);

        GetParent()->PostMessage(UWM_HTMLVIEW_CHANGE_ZOOM_MSG, vZoom.lVal);
        return;
    }

    CHtmlView::OnTimer(nIDEvent);
}

But the problem is that ExecWB expects a VARIANT * and I do not see how to pass this std::variant.


Solution

  • The diagnostic is correct, even though the advice is too generic to be useful. While std::variant is, in general, a great way to represent typesafe discriminated unions, it is unrelated to the VARIANT structure used in COM.

    In this situation you would need to use a different type, such as Microsoft's _variant_t class. It encapsulates the raw VARIANT, and deals with the internals of its discriminated union.

    It provides several constructors that properly manage setting the internal state, and derives from VARIANT so that any instance's address can be passed to any function that accepts a VARIANT*:

    #include <comutil.h>
    #pragma comment(lib, "comsuppw.lib")
    
    int main() {
        auto zoom{ _variant_t(long{ 0 }) };
        ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, nullptr, &zoom);
    }