How can I use WINRT c++ APIs in at Qt WINRT app?



  • How can I use WINRT c++ APIs in at Qt WINRT app?


  • Moderators

    Well, depending on the approach it's either smooth ride or a horror show. Here's a "world of pain" type example that gets current date using WRL and puts it on a Qt's label:

    #include <QApplication>
    #include <QLabel>
    
    #include <wrl/client.h>
    #include <wrl/wrappers/corewrappers.h>
    #include <windows.globalization.h>
    #include <windows.globalization.datetimeformatting.h>
    
    namespace WRL     = Microsoft::WRL;
    namespace Wrap    = Microsoft::WRL::Wrappers;
    namespace Fnd     = Windows::Foundation;
    namespace ABIFnd  = ABI::Windows::Foundation;
    namespace ABIGlob = ABI::Windows::Globalization;
    
    auto format = L"{year.full}-{month.integer(2)}-{day.integer(2)}";
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QLabel lbl;
        lbl.setAlignment(Qt::AlignCenter);
        lbl.show();
    
        //Qt initializes COM for us so we don't need to call RoInitialize
    
        WRL::ComPtr<ABIGlob::ICalendar> calendar;
        Wrap::HStringReference calendarClassId(RuntimeClass_Windows_Globalization_Calendar);
        if (SUCCEEDED(Fnd::ActivateInstance(calendarClassId.Get(), calendar.GetAddressOf())))
        {
           ABIFnd::DateTime dateTime;
           if (SUCCEEDED(calendar->GetDateTime(&dateTime)))
           {
               WRL::ComPtr<ABIGlob::DateTimeFormatting::IDateTimeFormatterFactory> dateTimeFormatterFactory;
               Wrap::HStringReference dateTimeFormatterId(RuntimeClass_Windows_Globalization_DateTimeFormatting_DateTimeFormatter);
               if (SUCCEEDED(Fnd::GetActivationFactory(dateTimeFormatterId.Get(), dateTimeFormatterFactory.GetAddressOf())))
               {
                   WRL::ComPtr<ABIGlob::DateTimeFormatting::IDateTimeFormatter> dateTimeFormatter;
                   Wrap::HStringReference dateTimeFormat(format);
                   if (SUCCEEDED(dateTimeFormatterFactory->CreateDateTimeFormatter(dateTimeFormat.Get(), dateTimeFormatter.GetAddressOf())))
                   {
                       Wrap::HString formattedDateTime;
                       if (SUCCEEDED(dateTimeFormatter->Format(dateTime, formattedDateTime.GetAddressOf())))
                       {
                           uint buff_len;
                           auto buff = formattedDateTime.GetRawBuffer(&buff_len);
    
                           lbl.setText(QString::fromWCharArray(buff, buff_len));
                       }
                   }
               }
           }
        }
    
        return a.exec();
    }
    

    And yes, before you manage to say "H O L Y    F U U U ..."  this is exactly the same as:

    #include <QApplication>
    #include <QLabel>
    #include <QDate>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QLabel lbl(QDate::currentDate().toString("yyyy-MM-dd"));
        lbl.setAlignment(Qt::AlignCenter);
        lbl.show();
    
        return a.exec();
    }
    

    To spare yourself from going absolutely insane you might want to look into wrapper libraries like Modern.



  • @Chris-Kawa Thanks! I was actually hoping to use the HoloLens gestures as Qt has major problems with mouse and clicking on HoloLens. Or if Qt can be fixed I wouldn't have to.

    I need to make the mouse follow and make clicking work on HoloLens. Like before Tuesday, haha!



  • Hi, thanks @Chris-Kawa for that example!

    Because I like to hurt myself I had to try it on my Windows 10 PC (worked fine).
    Just want to add, if you want to run it as well, you need to add:

    LIBS += -lruntimeobject
    

    to your .pro file.

    Edit: forgot to say: I tested with a vanilla Widgets app using the kit Desktop Qt 5.7.0 MSVC2015. However, if you instead select the Universal Windows Platform kit then that LIBS line above is not needed.


  • Moderators

    As much as I hate language extensions it would probably be easier to use C++/CX. It would make the example not much longer than that with Qt, but I can't seem to enable it.
    Passing /ZW and /AI <path to Platform.winmd> to the compiler (via QMAKE_CXXFLAGS in .pro file) should be enough AFAIK, but I get errors on as simple line as this:

    String^ foo = ref new String(L"Hello");
    

    error: C2664: 'long __winRT::__getActivationFactoryByPCWSTR(void *,Platform::Guid &,void **)': cannot convert argument 1 from 'const wchar_t []' to 'void *'
    Probably some switch still missing somewhere...



  • Hmm, never tried those pesky extensions, but being curious, I tried to reproduce your error but no luck :-( Maybe you're not on Windows 10 and MSVC2015?

    I got it to run with your example above, by adding #include <qdebug.h> and these lines just before return a.exec(); at the end:

    using namespace Platform;
    String^ foo = ref new String(L"Hello");
    qDebug() << QString::fromWCharArray(foo->Data());
    

    I tested both the 5.7.0 Desktop MSVC2015 32-bit and the Universal Windows Platform kit.

    And yeah, those /ZW and /AI switches (no space between /AI and dirname!) have to be in the .pro file:
    QMAKE_CXXFLAGS += /ZW /AI\"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcpackages\"


  • Moderators

    @hskoglund said:

    Maybe you're not on Windows 10 and MSVC2015?

    I'm on Win10 and MSVC2015 with Qt5.7UWP. Yeah, sorry, I mixed up my tests. The string alone works indeed, but try this full example and you should see the errors:

    #include <QApplication>
    #include <QLabel>
    
    using namespace Platform;
    using namespace Windows::Globalization;
    using namespace Windows::Globalization::DateTimeFormatting;
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        auto calendar = ref new Calendar();
        auto dateTime = calendar->GetDateTime();
        String^ format = L"{year.full}-{month.integer(2)}-{day.integer(2)}";
        auto dateTimeFormatter = ref new DateTimeFormatter(format);
        auto formatted = dateTimeFormatter->Format(dateTime);
    
        QLabel lbl(QString::fromWCharArray(formatted->Data()));
        lbl.setAlignment(Qt::AlignCenter);
        lbl.show();
    
        return a.exec();
    }
    


  • @Chris-Kawa @hskoglund

    Great information, Thanks!

    Unfortunately I don't think UWP HoloLens Gestures will work as it is 3D based and with a UWP app that's tough if not impossible. Well it would take some time and I have to have this project done before Tuesday. Probably not going to be possible.

    Unless either of you know how to do this:
    So the UWP apps are run in a container. Not sure if it is a Windows App Container or not but I want to be able to access the raw mouse & keyboard from the container level.

    Thanks so much for your help!



  • @Rad-B: Hi, I know games need to do this, and since some of them are UWP apps, raw mouse and keyboard access should be possible, for an example see stackoverflow

    @Chris-Kawa: Indeed this time I got the errors (2 of them):
    C:\Projects\untitled\main.cpp:23: error: C2664: 'long __winRT::__getActivationFactoryByPCWSTR(void *,Platform::Guid &,void **)': cannot convert argument 1 from 'const wchar_t [31]' to 'void *'

    Googling a bit it seems this interesting error has struck more people than you (i.e you have not forgotten any switch or something similar).

    So in conclusion I think this is a Monday morning bug and, while waiting for Microsoft to fix it (perhaps in Update 3?) you can always resort to a kludge, insert these fine lines of code just before int main(...:

    HRESULT __stdcall GetActivationFactoryByPCWSTR(void*,Guid&, void**);
    namespace __winRT
    {
        HRESULT __stdcall __getActivationFactoryByPCWSTR(const void* str, ::Platform::Guid& pGuid, void** ppActivationFactory)
        {
            return GetActivationFactoryByPCWSTR(const_cast<void*>(str), pGuid, ppActivationFactory);
        }
    }
    

    This will cast away that const problem for you :-)


  • Moderators

    @hskoglund Thanks for the workaround. I wonder how such a blatant bug could have slipped in :/

    @Rad-B I don't have HoloLens so I can only speculate, but looking at the examples it doesn't seem to be that different from my calendar example, you just use the classes from the Windows::UI::Input::Spatial namespace. It seems the C++/CX is the easiest here, so you will probably have to use the workaround @hskoglund provided.
    See here for some code examples (the SpatialInputHandler.h/.cpp files).



  • @Chris-Kawa @hskoglund How can I access Windows.UI.Core.CoreWindow?

    namespace Windows::UI does not seem to work.

    Thanks for both of your help!



  • @Rad-B Sorry for being a bit late to the discussion, but input on Hololens should have been fixed by https://codereview.qt-project.org/#/c/170653/ . At least we got feedback on JIRA, that this resolves input problems...


  • Moderators

    Hey, good news! After years of begging Microsoft released a WinRT C++ library that doesn't suck like WRL: C++/WinRT

    It's actually kinda simple to use. Close to Qt if not for the long names and namespaces. I gave it a whirl with the calendar example:

    #include <QApplication>
    #include <QLabel>
    #include "winrt/Windows.Globalization.h"
    #include "winrt/Windows.Globalization.DateTimeFormatting.h"
    
    #pragma comment(lib, "windowsapp")
    using namespace winrt::Windows::Globalization;
    using namespace winrt::Windows::Globalization::DateTimeFormatting;
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        std::wstring formatted = DateTimeFormatter(L"{year.full}-{month.integer(2)}-{day.integer(2)}").Format(Calendar().GetDateTime());
    
        QLabel lbl(QString::fromWCharArray(formatted.c_str()));
        lbl.setAlignment(Qt::AlignCenter);
        lbl.show();
    
        return a.exec();
    }
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.