【问题标题】:Proper way to create a WinRT object in C在 C 中创建 WinRT 对象的正确方法
【发布时间】:2021-06-08 03:20:32
【问题描述】:

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

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

由于我没有错误代码映射,我不知道哪个部分出错了。 那么谁能告诉我在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 对象来枚举设备的其他属性。

【问题讨论】:

  • 这个类是不可创建的。 DeviceInformation 也不是。您可以从 IDeviceInformationStatics::CreateWatcher 开始(如这里所示 docs.microsoft.com/en-us/uwp/api/…)。这是一个类似的 C 代码:pastebin.com/raw/HxAb1P8A
  • @SimonMourier,如果这不是可创建的,那么我应该在需要这个对象的函数中输入什么。由于 WinRT 代码中也有类似的情况,例如用于枚举附加参数的 IVector 对象。如果 C++ 代码有某种结构来创建这个对象,那么在 C 中不应该有一些替代方法吗?
  • 系统应该为您提供一个,例如来自 IDeviceInformationStatics::CreateFromIdAsync
  • @SimonMourier,问题是没有这样的功能/我找不到这些对象的这样的功能
  • 显示您想要移植到 C 的完整 C++ 部分

标签: c windows-runtime c++-winrt


【解决方案1】:

好的,经过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对象,有兴趣或还不明白我的答案的人可以看看。

【讨论】:

  • 这确实有效,但它掩盖了一些至关重要的细节:1 函数的实际名称并不重要。您使用的那些似乎是由工具生成的。 2 更重要的是,通用接口的 IID 是计算的。它们不仅是随机生成的,而且是从所涉及类型的 IID 派生而来的。
【解决方案2】:

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

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

首先声明你的对象结构——你可以用一个对象实现多个接口。不要忘记为结束标记添加空格。

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

第三创建您的实际对象-您也可以使用malloc-如果您决定嵌入任何数据(在markend之后-设置shouldbefreed并且您的初始对象计数为0以便您的对象自动释放):

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

4th 使用您的任何对象接口 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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-22
    • 2017-11-01
    • 1970-01-01
    • 2022-01-07
    • 2010-10-11
    • 1970-01-01
    相关资源
    最近更新 更多