Search code examples
cwindows-runtimec++-winrt

Proper way to create a WinRT object in C


Does anyone know what is the proper way to create a WinRT object in C? I'm trying to convert my C++ code that uses WinRT API to plain C code. And right now I'm able to get a few WinRT static functions working. However, for the objects required by the static function, like the __FIAsyncOperation_1_Windows_CDevicesCEnumerationCDeviceInformation for the get_Completed function in FIAsyncOperation_1_WindowsCDevicesCHumanInterfaceDevice_CHidDeviceVtbl, I can't find a proper way to create the object. First, I can't find the iid of this object in the idl file. Second, I'm not sure about the namespace of the object.

I did find how this class being declare in C++ macro,

#ifndef DEF___FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation_USE
 #define DEF___FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation_USE
 #if !defined(RO_NO_TEMPLATE_NAME)
 namespace ABI { namespace Windows { namespace Foundation {
 template <>
 struct __declspec(uuid("07faa053-eb2f-5cba-b25b-d9d57be6715f"))
 IAsyncOperation<ABI::Windows::Devices::Enumeration::DeviceInformation*> : IAsyncOperation_impl<ABI::Windows::Foundation::Internal::AggregateType<ABI::Windows::Devices::Enumeration::DeviceInformation*, ABI::Windows::Devices::Enumeration::IDeviceInformation*>>
 {
     static const wchar_t* z_get_rc_name_impl()
     {
         return L"Windows.Foundation.IAsyncOperation`1<Windows.Devices.Enumeration.DeviceInformation>";
     }
 };
 // Define a typedef for the parameterized interface specialization's mangled name.
 // This allows code which uses the mangled name for the parameterized interface to access the
 // correct parameterized interface specialization.
 typedef IAsyncOperation<ABI::Windows::Devices::Enumeration::DeviceInformation*> __FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation_t;
 #define __FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation ABI::Windows::Foundation::__FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation_t
 /* Foundation */ } /* Windows */ } /* ABI */ }
    
 #endif // !defined(RO_NO_TEMPLATE_NAME)
 #endif /* DEF___FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation_USE */

So, I tried to use this uuid and name_impl here to create the object, like this

             namespace = L"Windows.Foundation.IAsyncOperation`1<Windows.Devices.Enumeration.DeviceInformation>";
             hr = WindowsCreateStringReferenceFunc(namespace, (UINT32)wcslen(namespace), &namespace_string_header, &namespace_string);
             static const IID async_iid = { 0x07faa053, 0xeb2f, 0x5cba, { 0xb2, 0x5b, 0xd9, 0xd5, 0x7b, 0xe6, 0x71, 0x5f } };
             if (SUCCEEDED(hr)) {
                 __FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation* test;
                 hr = RoGetActivationFactoryFunc(namespace_string, &async_iid, &test);
                 if (!SUCCEEDED(hr)) {
                     printf("Couldn't find Windows.Foundation.IAsyncOperation`1<Windows.Devices.Enumeration.DeviceInformation>: %d\n", hr);
                 }
             }

And after the build, the program return

Couldn't find Windows.Foundation.IAsyncOperation`1<Windows.Devices.Enumeration.DeviceInformation>: -2147221164

As I don't have the error code mapping, I don't know which part goes wrong. So can anyone tell me the correct way to create an object of WinRT in c?

I had asked this question in the Microsoft forum and they replied their Q&A currently not support this type of question. And I had also read this question before, but the answer cannot solve my problem.

Update1: This is the code that I want to convert from C++ to C

        hstring selector = winrt::to_hstring("System.Devices.InterfaceClassGuid:=\"{4D1E55B2-F16F-11CF-88CB-001111000030}\"") +
                            winrt::to_hstring(" System.DeviceInterface.Hid.VendorId: = ") + winrt::to_hstring(0x0d28) +
                            winrt::to_hstring(" AND System.DeviceInterface.Hid.ProductId : = ") + winrt::to_hstring(0x0204);
        Windows::Foundation::Collections::IVector<hstring> prop{ winrt::single_threaded_vector<hstring>() };
        prop.Append(to_hstring("System.ItemNameDisplay"));
        prop.Append(to_hstring("System.Devices.DeviceInstanceId"));
        prop.Append(to_hstring("System.Devices.Parent"));
        prop.Append(to_hstring("System.Devices.LocationPaths"));
        prop.Append(to_hstring("System.Devices.Children"));
        prop.Append(to_hstring("System.Devices.DeviceManufacturer"));
        DeviceInformationCollection collection = DeviceInformation::FindAllAsync(selector, prop).get();

As there is no get function in C, I would need to create an async object to handle the async operation in C. I also need to create a IVector object to enumerate the additional properties of the devices.


Solution

  • Okay, so after a few investigations from a GitHub repo and some help from the comment, I found the answer to my question. There is actually no contrustor function for objects like the __FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation What I need to do is to implement the functions listed in the vtbl struct. For example when I want to have a object of __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection, I will need to implement the functions listed in __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollectionVtbl, which are

    typedef struct __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollectionVtbl
    {
        BEGIN_INTERFACE
    
        HRESULT (STDMETHODCALLTYPE* QueryInterface)(__FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* This,
            REFIID riid,
            void** ppvObject);
        ULONG (STDMETHODCALLTYPE* AddRef)(__FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* This);
        ULONG (STDMETHODCALLTYPE* Release)(__FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* This);
        HRESULT (STDMETHODCALLTYPE* Invoke)(__FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* This,
            __FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* asyncInfo,
            AsyncStatus asyncStatus);
    
        END_INTERFACE
    } __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollectionVtbl;
    

    So I should have something like this

    static HRESULT STDMETHODCALLTYPE async_query_interface(__FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* This,
        REFIID riid,
        void** ppvObject)
    {
        if (!ppvObject) {
            return E_INVALIDARG;
        }
    
        *ppvObject = NULL;
        static const IID async_iid = { 0x4a458732, 0x527e, 0x5c73, { 0x9a, 0x68, 0xa7, 0x3d, 0xa3, 0x70, 0xf7, 0x82 } };
        if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &async_iid)) {
            *ppvObject = This;
            This->lpVtbl->AddRef(This);
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    
    static ULONG STDMETHODCALLTYPE async_add_ref(__FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* This)
    {
        return 1;
    }
    
    static ULONG STDMETHODCALLTYPE async_release(__FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* This)
    {
        return 1;
    }
    
    static HRESULT STDMETHODCALLTYPE async_invoke(__FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* This,
        __FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection* asyncInfo,
        AsyncStatus asyncStatus)
    {
        //The callback when the async complete or have some problem
    }
    

    And the iid here static const IID async_iid = { 0x4a458732, 0x527e, 0x5c73, { 0x9a, 0x68, 0xa7, 0x3d, 0xa3, 0x70, 0xf7, 0x82 } }; can be found in the header file in the winrt folder(windows.devices.enumeration.h),

    namespace ABI { namespace Windows { namespace Foundation {
    template <>
    struct __declspec(uuid("4a458732-527e-5c73-9a68-a73da370f782"))
    IAsyncOperationCompletedHandler<ABI::Windows::Devices::Enumeration::DeviceInformationCollection*> : IAsyncOperationCompletedHandler_impl<ABI::Windows::Foundation::Internal::AggregateType<ABI::Windows::Devices::Enumeration::DeviceInformationCollection*, __FIVectorView_1_Windows__CDevices__CEnumeration__CDeviceInformation*>>
    {
        static const wchar_t* z_get_rc_name_impl()
        {
            return L"Windows.Foundation.AsyncOperationCompletedHandler`1<Windows.Devices.Enumeration.DeviceInformationCollection>";
        }
    };
    

    And when all these been prepared, I just need to do this to get the __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection object

        __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection async_op;
        __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollectionVtbl async_vtbl = {
            .QueryInterface = async_query_interface,
            .AddRef = async_add_ref,
            .Release = async_release,
            .Invoke = async_invoke,
        };
        async_op.lpVtbl = &async_vtbl;
        hr = async_dev_collection->lpVtbl->put_Completed(async_dev_collection, &async_op);
    

    For the example that I found my answer, it uses this method to create a __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController object, anyone who interested or still don't understand from my answer can have a look.