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

Viewed 100

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.

2 Answers

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);
}

Sorry you can't use std::variant here.

VARIANT is a type used in COM for interoperation of different components, even if written in different languages and residing in different processes.

std::variant provides type-safe variant of arbitrary set of types that is passed as a template parameters. Even two std::variants are incompatible if they have different template parameters, and none of them is compatible with VARIANT.

The best way you can make your program more robust is using CComVariant from ATL, or find/create another wrapper for VARIANT structure. Not sure if it would make the warning go away though.

Related