Правильный способ создания объекта WinRT в C

Кто-нибудь знает, как правильно создать объект WinRT в C? Я пытаюсь преобразовать свой код C++, использующий WinRT API, в простой код C. И прямо сейчас я могу заставить работать несколько статических функций WinRT. Однако для объектов, требуемых статической функцией, таких как __FIAsyncOperation_1_Windows_CDevicesCEnumerationCDeviceInformation для функции get_Completed в FIAsyncOperation_1_WindowsCDevicesCHumanInterfaceDevice_CHidDeviceVtbl, я не могу найти правильный способ создания объекта. Во-первых, я не могу найти идентификатор этого объекта в файле idl. Во-вторых, я не уверен в пространстве имен объекта.

Я нашел, как этот класс объявляется в макросе C++,

#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 */

Итак, я попытался использовать этот uuid и name_impl для создания объекта, например

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

И после сборки программа возвращается

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

Поскольку у меня нет сопоставления кода ошибки, я не знаю, какая часть пошла не так. Так может ли кто-нибудь сказать мне, как правильно создать объект WinRT в c?

Я задал этот вопрос в форум Microsoft, и они ответили, что в настоящее время вопросы и ответы не поддерживают этот тип вопросов. И я также читал этот вопрос раньше, но ответ не может решить мою проблему.

Update1: это код, который я хочу преобразовать из C++ в 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();

Поскольку в C нет функции get, мне нужно создать асинхронный объект для обработки асинхронной операции в C. Мне также нужно создать объект IVector для перечисления дополнительных свойств устройств.


person Leslie    schedule 08.06.2021    source источник
comment
Этот класс нельзя создать. DeviceInformation тоже нет. Вы можете начать с IDeviceInformationStatics::CreateWatcher (как показано здесь docs.microsoft.com/en-us/uwp/api/). Вот аналогичный код C: pastebin.com/raw/HxAb1P8A   -  person Simon Mourier    schedule 08.06.2021
comment
@SimonMourier, если это невозможно создать, то что мне следует ввести в функцию, для которой требуется этот объект. Так как в коде WinRT есть подобные случаи, например, объекты IVector для перечисления дополнительного параметра. И если есть какая-то структура для кода C++ для создания этого объекта, то не должна ли быть какая-то альтернатива для этого в C?   -  person Leslie    schedule 08.06.2021
comment
Система должна предоставить вам его, например, из IDeviceInformationStatics::CreateFromIdAsync.   -  person Simon Mourier    schedule 08.06.2021
comment
@SimonMourier, проблема в том, что такой функции нет / я не могу найти такую ​​функцию для этого объекта   -  person Leslie    schedule 08.06.2021
comment
Покажите полную часть C++, которую вы хотите перенести на C   -  person Simon Mourier    schedule 08.06.2021
comment
@SimonMourier, вопрос обновлен   -  person Leslie    schedule 09.06.2021
comment
Это именно то, что я сказал и показал в своем примере кода. FindAllAsyncAqsFilterAndAdditionalProperties (соответствует FindAllAsync, поскольку C++/WinRT предоставляет удобные перегрузки с одинаковыми именами) — это метод IDeviceInformationStatics, который возвращает вам асинхронный объект. Однако для векторов вы должны реализовать (предоставить функции vtable) свой собственный __FIIterable_1_HSTRING, поскольку AFAIK, Microsoft не предоставляет создаваемый векторный класс. C++/WinRT предоставляет один (например, single_threaded_vector). Другими словами, использование WinRT с C технически возможно, но далеко не просто (т. е. безумие).   -  person Simon Mourier    schedule 09.06.2021
comment
Вам нужно будет изучить, как дженерики выставляются в ABI. Я считаю, что для этого требуется вычислить IID универсального интерфейса. В репозитории C++/WinRT есть код, который это делает.   -  person IInspectable    schedule 11.06.2021
comment
@SimonMourier, спасибо за вашу помощь, вы правы, что мне нужно предоставить свои собственные функции для vtable, чтобы это работало. Хотя я на самом деле не понимаю, что вы имели в виду, когда я прочитал ваш комментарий, XD мне немного сложно понять без примера.   -  person Leslie    schedule 11.06.2021


Ответы (2)


Итак, после нескольких исследований из репозитория GitHub и некоторой помощи из комментария, я нашел ответ на мой вопрос. На самом деле нет функции contrustor для таких объектов, как __FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation. Что мне нужно сделать, так это реализовать функции, перечисленные в структуре vtbl. Например, когда я хочу иметь объект __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection, мне нужно будет реализовать функции, перечисленные в __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollectionVtbl, которые

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;

Так что у меня должно быть что-то вроде этого

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
}

И iid здесь static const IID async_iid = { 0x4a458732, 0x527e, 0x5c73, { 0x9a, 0x68, 0xa7, 0x3d, 0xa3, 0x70, 0xf7, 0x82 } }; можно найти в заголовочном файле в папке winrt(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>";
    }
};

И когда все это будет подготовлено, мне просто нужно сделать это, чтобы получить объект __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CEnumeration__CDeviceInformationCollection

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

Для примера, который я нашел мой ответ, он использует этот метод для создания объекта __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController, любой, кто заинтересован или все еще не понял из моего ответа, может посмотреть.

person Leslie    schedule 11.06.2021
comment
Это работает, конечно, но некоторые жизненно важные детали замалчиваются: 1 Настоящие имена функций не важны. Те, которые вы используете, похоже, были созданы инструментами. 2 Что еще более важно, IID для универсальных интерфейсов вычисляются. Они не просто генерируются случайным образом, а скорее выводятся из IID задействованных типов. - person IInspectable; 12.06.2021

По сути, я создал небольшую инфраструктуру, как показано в этом примере для использования UWP с простым C.

Для реализации собственных обработчиков вам потребуется несколько шагов.

1-й объявите свою структуру объекта - вы можете реализовать несколько интерфейсов с одним объектом. Не забудьте добавить место для конечной отметки.

struct bthgenhandler {
    struct stdifaceopts;
    __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CBluetooth__CBluetoothDevice;
    struct stdiftrailer;
    char* markend;
};

Для простого интерфейса вы можете просто встроить структуры внутрь (и если вы используете MSVC).

2-й инициализируйте vtable ваших интерфейсов и то, что она реализует (в то время как для второго вам понадобится дополнительный завершающий элемент):

    static __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CBluetooth__CBluetoothDeviceVtbl bthfromaddrhandlervtbl = { QueryInterface, AddRef, Release, bthgetRfcommsrvcsInvoke };
    
    static const IID* bthgetRfcommsrvcsimplements[] = { &IID_IUnknown, &IID___FIAsyncOperationCompletedHandler_1_Windows__CDevices__CBluetooth__CRfcomm__CRfcommDeviceServicesResult, 0 };

3. Создайте свой фактический объект — вы также можете использовать malloc — если вы решили внедрить какие-либо данные (после markend — установите shouldbefreed, и ваши исходные объекты будут считаться 0, чтобы ваш объект был автоматически освобожден):

static struct bthgenhandler bthgetRfcommsrvcshandler = { .lpVtbl = &bthfromaddrhandlervtbl, bthgetRfcommsrvcsimplements, 1, &bthgetRfcommsrvcshandler, };

4. Вызовите метод, требующий вашего объекта обратного вызова, с любым из ваших интерфейсов объекта vtbls (QueryInterface все равно найдет правильный интерфейс - в настоящее время у нас есть только 1 в этом примере):

__FIAsyncOperation_1_Windows__CDevices__CBluetooth__CRfcomm__CRfcommDeviceServicesResult_put_Completed(asyncop, &bthgetRfcommsrvcshandler.lpVtbl);

Теперь для IIDS - то, что я придумал, это отдельный заголовок, в который я копирую UIID из части C++ заголовка в виде строк (например:)

GEN_IID_FROM_STRING("522c25d1-866b-5de4-bd8e-1feb5ae60d47", __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CBluetooth__CRfcomm__CRfcommDeviceServicesResult)

Где "522c25d1-866b-5de4-bd8e-1feb5ae60d47" происходит от windows.devices.bluetooth.h.

И, наконец, для активации объектов у вас есть два помощника - activateclassdirect и activateclasslight - иногда, если первый не работает, попробуйте другой.

person AnArrayOfFunctions    schedule 13.07.2021