Search code examples
c++windows-runtimewinrt-xamlc++-cxc++-winrt

Error C2664 'HRESULT IUnknown::QueryInterface(const IID &,void **)': cannot convert argument 1 from 'const winrt::guid' to 'const IID &'


This error happens to me when I use the helper function from microsoft docs to migrate to winrt from cx. I see a similar question here, but the solution mentioned doesn't seem to work for me. The solution mentioned here add #include <Unknwn.h> before any other winrt headers in the file which has this error.

template <typename T>
T from_cx(Platform::Object ^ from) {
T to{nullptr};

winrt::check_hresult(reinterpret_cast<::IUnknown*>(from)->QueryInterface(
    winrt::guid_of<T>(), reinterpret_cast<void**>(winrt::put_abi(to))));

return to;
}

This is the entire file:

#pragma once

#include <Unknwn.h>
#include <winrt/Windows.Foundation.h>

namespace x {
namespace y {

template <typename T>
T from_cx(Platform::Object ^ from) {
    T to{nullptr};

    winrt::check_hresult(reinterpret_cast<::IUnknown*>(from)->QueryInterface(
        winrt::guid_of<T>(), reinterpret_cast<void**>(winrt::put_abi(to))));

    return to;
}

template <typename T>
    T ^
    to_cx(winrt::Windows::Foundation::IUnknown const& from) {
        return safe_cast<T ^>(reinterpret_cast<Platform::Object ^>(winrt::get_abi(from)));
    }
}
}

Solution

  • winrt::guid_of() returns a winrt::guid. Per What's new in C++/WinRT:

    • Breaking change. GUID is now projected as winrt::guid. For APIs that you implement, you must use winrt::guid for GUID parameters. Otherwise, winrt::guid converts to GUID, as long as you include unknwn.h before you include any C++/WinRT headers. See Interoperating with the ABI's GUID struct.

    Per Interoperating with the ABI's GUID struct:

    GUID is projected as winrt::guid. For APIs that you implement, you must use winrt::guid for GUID parameters. Otherwise, there are automatic conversions between winrt::guid and GUID as long as you include unknwn.h (implicitly included by <windows.h> and many other header files) before you include any C++/WinRT headers.

    If you don't do that, then you can hard-reinterpret_cast between them.

    So, either make sure unknwn.h is included before WinRT headers, or else you can reinterpret_cast explicitly, eg:

    template <typename T>
    T from_cx(Platform::Object ^ from) {
        T to{nullptr};
        winrt::guid iid = winrt::guid_of<T>();
    
        winrt::check_hresult(
            reinterpret_cast<::IUnknown*>(from)->QueryInterface(
                reinterpret_cast<GUID&>(iid),
                reinterpret_cast<void**>(winrt::put_abi(to)))
        );
    
        return to;
    }