在 C 中创建 WinRT 对象的正确方法

Proper way to create a WinRT object in C

有谁知道在 C 中创建 WinRT 对象的正确方法是什么? 我正在尝试将使用 WinRT API 的 C++ 代码转换为纯 C 代码。 现在我可以让一些 WinRT 静态函数正常工作。 但是对于静态函数需要的对象,比如__FIAsyncOperation_1_Windows_CDevicesCEnumerationCDeviceInformation对于FIAsyncOperation_1_WindowsCDevicesCHumanInterfaceDevice_CHidDeviceVtbl中的get_Completed函数,我找不到合适的方法来创建对象。 首先,我在idl文件中找不到这个对象的iid。 其次,我不确定对象的命名空间。

我确实发现了这个 class 是如何在 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);
                 }
             }

构建之后,程序 return

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

由于我没有错误代码映射,所以我不知道是哪个部分出了问题。 那么谁能告诉我在 c 中创建 WinRT 对象的正确方法?

我在 Microsoft forum 中问过这个问题,他们的问答目前不支持此类问题。 而且我之前也看过this question,但是答案不能解决我的问题。

更新1: 这是我想从 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 对象来枚举设备的附加属性。

好的,在 a GitHub repo 的一些调查和评论的一些帮助之后,我找到了问题的答案。 __FIAsyncOperation_1_Windows__CDevices__CEnumeration__CDeviceInformation 这样的对象实际上没有 contrustor 函数 我需要做的是实现 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
}

而这里的iidstatic 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);

我找到答案的example,就是用这个方法创建了一个__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController对象,有兴趣或者不明白的可以看看我的答案。

基本上,我已经创建了一个小型基础架构,如此 sample 中所示,用于使用纯 C 使用 UWP。

要实现您自己的处理程序,您需要几个步骤。

首先声明您的 object 结构 - 您可以使用一个 object 实现多个接口。不要忘记为结束标记添加 space。

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

对于简单的界面,您可以将结构嵌入其中(如果您使用的是 MSVC)。

第二次初始化您的接口 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 };

3rd 创建您的实际 object - 您也可以使用 malloc - 如果您决定嵌入任何数据(在 markend 之后 - 设置 shouldbefreed 和您的初始 object 数到 0 以便您的 object 自动释放):

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

4th 使用任何 object 接口 vtbls 调用需要回调 object 的方法(QueryInterface 无论如何都会找到正确的接口 - 目前我们在这个例子中只有 1 个):

__FIAsyncOperation_1_Windows__CDevices__CBluetooth__CRfcomm__CRfcommDeviceServicesResult_put_Completed(asyncop, &bthgetRfcommsrvcshandler.lpVtbl);

现在对于 IIDS - 我想到的是一个单独的 header,我从 header 的 C++ 部分将 UIID 复制为字符串(例如:)

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

最后,为了激活 object,您有两个助手 - activateclassdirectactivateclasslight - 有时如果第一个不起作用,请尝试另一个。