公司网站建设价位,网站中的二维码设计,如何创建一个互联网平台,网站广告调词平台如果 API 位于 Windows 命名空间中
这是你使用 Windows 运行时 API 最常见的情况。 对于元数据中定义的 Windows 命名空间中的每个类型#xff0c;C/WinRT 都定义了 C 友好等效项#xff08;称为投影类型 #xff09;。 投影类型具有与 Windows 类型相同的完全限定名称C/WinRT 都定义了 C 友好等效项称为投影类型 。 投影类型具有与 Windows 类型相同的完全限定名称但使用 C 语法放置于 C winrt 命名空间中。 例如Windows::Foundation::Uri 作为 winrt::Windows::Foundation::Uri 投影到 C/WinRT。
以下是一个简单的代码示例。 若要将以下代码示例直接复制并粘贴到 Windows 控制台应用程序 (C/WinRT) 项目的主源代码文件中请先在项目属性中设置“不使用预编译的标头” 。
// main.cpp
#include winrt/Windows.Foundation.husing namespace winrt;
using namespace Windows::Foundation;int main()
{winrt::init_apartment();Uri contosoUri{ Lhttp://www.contoso.com };Uri combinedUri contosoUri.CombineUri(Lproducts);
}包含的标头 winrt/Windows.Foundation.h 是 SDK 的一部分可在文件夹 %WindowsSdkDir%IncludeWindowsTargetPlatformVersion\cppwinrt\winrt\ 内找到。 该文件夹中的标头包含投影到 C/WinRT 中的 Windows 命名空间类型。 在此示例中winrt/Windows.Foundation.h 包含 winrt::Windows::Foundation::Uri 它是运行时类 Windows::Foundation::Uri 的投影类型。 提示
如果希望使用来自 Windows 命名空间的类型请包括与该命名空间对应的 C/WinRT 标头。 using namespace 指令是可选的不过这种指令很方便。
在上述代码示例中在初始化 C/WinRT 后我们将通过其公开记录的构造函数之一本示例中为 Uri(字符串) 堆叠分配 winrt::Windows::Foundation::Uri 投影类型的值。 这是最常见的用例也是一般情况下你所要做的全部工作。 在有了 C/WinRT 投影类型值后你可以将其视为实际 Windows 运行时类型的实例因为它具有所有相同的成员。
事实上该投影值是一个代理它本质上只是支持对象的智能指针。 投影值的构造函数调用 RoActivateInstance 来创建 Windows 运行时支持类本例中为 Windows.Foundation.Uri 的实例并将该对象的默认接口存储在新投影值内。 如下所示你对投影值的成员的调用实际上通过智能指针代理给支持对象这是发生状态变化的地方。 当 contosoUri 值超出范围时它将自行销毁并将其引用发布到默认接口。 如果该引用是对支持 Windows 运行时 Windows.Foundation.Uri 对象的最后一个引用支持对象也会自行销毁。 提示
投影类型 是出于使用运行时类的 API 针对运行时类的包装器。 投影接口 是针对 Windows 运行时接口的包装器。 C/WinRT 投影标头
若要使用 C/WinRT 的 Windows 命名空间 API你应使用来自 %WindowsSdkDir%IncludeWindowsTargetPlatformVersion\cppwinrt\winrt 文件夹的标头。 下级命名空间中的类型在其直接父命名空间中引用类型是很常见的。 因此每个 C/WinRT 投影标头将自动包括其父命名空间标头文件因此你不需要 明确包括它。 不过这样做也不会出现错误。
例如对于 Windows::Security::Cryptography::Certificates 命名空间等效的 C/WinRT 类型定义驻留在 winrt/Windows.Security.Cryptography.Certificates.h 中。 Windows::Security::Cryptography::Certificates 中的类型需要父 Windows::Security::Cryptography 命名空间中的类型该命名空间中的类型可能需要其自己的父 Windows::Security 中的类型。
因此当你包括 winrt/Windows.Security.Cryptography.Certificates.h 时该文件反之将包括 winrt/Windows.Security.Cryptography.h且 winrt/Windows.Security.Cryptography.h 将包括 winrt/Windows.Security.h。 这就是跟踪停止的位置因为没有 winrt/Windows.h。 此可传递包含过程在第二级命名空间停止。
此过程间接包括为在父命名空间中定义的类提供必要的声明 和实现 的标头文件。
一个命名空间中类型的成员可以引用其他无关命名空间中的一个或多个类型。 为了让编译器能够成功编译这些成员定义编译器需要查看所有这些类型关闭的类型声明。 因此每个 C/WinRT 投影标头均包括它需要声明 任何依赖类型的命名空间标头。 与父命名空间不同此过程不 拉入所引用类型的实现 。 重要
当你希望实际使用 在无关命名空间中声明的类型实例化、调用方法等时你必须包括该类型的相应的命名空间标头文件。 仅自动包括声明 没有实现 。
例如如果你仅包括 winrt/Windows.Security.Cryptography.Certificates.h则会导致声明从这些命名空间等等间接拉入。
Windows.FoundationWindows.Foundation.CollectionsWindows.NetworkingWindows.Storage.StreamsWindows.Security.Cryptography
换言之某些 API 在你包括的标头中进行了前向声明。 但它们的定义在你尚未包括的标头中。 因此如果你随后调用 Windows::Foundation::Uri::RawUri 那么你会收到指示成员未定义的链接器错误。 解决方法是明确 #include winrt/Windows.Foundation.h。 一般情况下当你看到此类链接器错误时应包括为该 API 的命名空间命名的标头并重新生成。 通过对象、接口或通过 ABI 访问成员
使用 C/WinRT 投影Windows 运行时类的运行时表示形式将只是基础 ABI 接口。 不过为方便起见你可以通过类作者预期的方式根据类编码。 例如你可以调用 Uri 的 ToString 方法就像它是该类的方法实际上再深入一层它是单独的 IStringable 接口上的方法。
WINRT_ASSERT 是宏定义并且扩展到 _ASSERTE。
Uri contosoUri{ Lhttp://www.contoso.com };
WINRT_ASSERT(contosoUri.ToString() Lhttp://www.contoso.com/); // QueryInterface is called at this point.这种便利是通过对相应接口的查询实现的。 不过始终在你的控制范围内。 你可以通过自己检索 IStringable 接口并直接使用它来放弃一点便利从而提高一点性能。
在下方的代码示例中你在运行时获取实际的 IStringable 接口指针通过一次性查询。 此后你对 ToString 的调用是直接的并且避免进一步调用 QueryInterface。
...
IStringable stringable contosoUri; // One-off QueryInterface.
WINRT_ASSERT(stringable.ToString() Lhttp://www.contoso.com/);如果你知道你将在同一个接口调用多个方法你可能会选择使用此技巧。
顺便说一下如果你确实希望访问 ABI 级别的成员那么你可以这样做。
下方的代码示例显示了如何操作实现 C/WinRT 与 ABI 之间的互操作中还提供了更多详细信息和代码示例。
#include Windows.Foundation.h
#include unknwn.h
#include winrt/Windows.Foundation.h
using namespace winrt::Windows::Foundation;int main()
{winrt::init_apartment();Uri contosoUri{ Lhttp://www.contoso.com };int port{ contosoUri.Port() }; // Access the Port property accessor via C/WinRT.winrt::com_ptrABI::Windows::Foundation::IUriRuntimeClass abiUri{contosoUri.asABI::Windows::Foundation::IUriRuntimeClass() };HRESULT hr abiUri-get_Port(port); // Access the get_Port ABI function.
}延迟初始化
在 C/WinRT 中每个投影的类型都有一个特殊的 C/WinRT std::nullptr_t 构造函数。 除了该构造函数所有其他投影类型的构造函数包括默认的构造函数都会导致系统创建一个支持的 Windows 运行时对象并为你提供它的智能指针。 因此该规则适用于使用默认构造函数的任何地方例如未初始化的本地变量、未初始化的全局变量以及未初始化的成员变量。
另一方面如果你想要构造投影类型的变量而无需它反过来构造支持的 Windows 运行时对象以便你可以延迟该工作你可以这样做。 使用该特殊 C/WinRT std::nullptr_t 构造函数C/WinRT 投影已将它插入每个运行时类中声明你的变量或字段。
在下面的代码示例中我们将该特殊构造函数与 m_gamerPicBuffer 配合使用。
#include winrt/Windows.Storage.Streams.h
using namespace winrt::Windows::Storage::Streams;#define MAX_IMAGE_SIZE 1024struct Sample
{void DelayedInit(){//分配实际缓冲区,延迟初始化m_gamerPicBuffer Buffer(MAX_IMAGE_SIZE);}private:Buffer m_gamerPicBuffer{ nullptr };//使用该特殊 C/WinRT std::nullptr_t 构造函数C/WinRT 投影已将它插入每个运行时类中声明你的变量或字段
};int main()
{winrt::init_apartment();Sample s;// ...s.DelayedInit();
}除 std::nullptr_t 构造函数以外的投影类型上的所有构造函数都将导致创建后备 Windows 运行时对象。 std::nullptr_t 构造函数本质上不执行任何操作。 它预期投影对象会在后续时间初始化。 因此不论运行时类是否具有默认的构造函数你都可以使用此技巧实现有效的延迟初始化。 此注意事项会影响在其中调用默认构造函数的其他位置如在向量和映射中。
请考虑此代码示例对它需要“空白应用(C/WinRT)” 项目。
std::mapint, TextBlock lookup;
lookup[2] value;进行分配会创建新 TextBlock 然后立即使用 value 覆盖它。 下面是补救措施。
std::mapint, TextBlock lookup;
lookup.insert_or_assign(2, value);另请参阅默认构造函数如何影响集合。 不要错误地延迟初始化
请注意不要错误地调用 std:: nullptr_t 构造函数。 编译器的冲突解决偏向于它而非工厂构造函数。 例如请考虑这两个运行时类定义。
idl
// GiftBox.idl
runtimeclass GiftBox
{GiftBox();
}// Gift.idl
runtimeclass Gift
{Gift(GiftBox giftBox); // You can create a gift inside a box.
}假设我们想要构造一个不在盒子内的 Gift使用未初始化的 GiftBox 构造的 Gift。 首先让我们看看错误的做法。 我们知道有一个接受 GiftBox 的 Gift 构造函数。 但是如果想要传递 null GiftBox通过统一初始化调用 Gift 构造函数如下所示则不会获得我们想要的结果 。
// These are *not* what you intended. Doing it in one of these two ways
// actually *doesnt* create the intended backing Windows Runtime Gift object;
// only an empty smart pointer.Gift gift{ nullptr };
auto gift{ Gift(nullptr) };在此处得到的是一个未初始化的 Gift。 你无法通过未初始化的 GiftBox 得到 Gift。 下面是正确 的做法。
// Doing it in one of these two ways creates an initialized
// Gift with an uninitialized GiftBox.Gift gift{ GiftBox{ nullptr } };
auto gift{ Gift(GiftBox{ nullptr }) };在不正确的示例中传递 nullptr 文本会以有利于延迟初始化构造函数的方式解析。 若要以有利于工厂构造函数的方式解析参数的类型必须是 GiftBox。 仍然可以选择传递一个显式延迟初始化 GiftBox如正确的示例中所示。 下一个示例也正确 因为参数的类型为 GiftBox而不是 std:: nullptr_t。
GiftBox giftBox{ nullptr };
Gift gift{ giftBox }; // Calls factory constructor.仅当传递 nullptr 文本时才会引起多义性。 不要错误地复制构造。
此警告类似于上面的不要错误地延迟初始化部分中所述的警告。
除了该延迟初始化构造函数之外C/WinRT 投影也会将复制构造函数注入到每个运行时类中。 它是一个单参数构造函数接受与所构造对象相同的类型。 生成的智能指针指向其构造函数参数指向的同一后备 Windows 运行时对象。 结果是两个智能指针对象指向同一后备对象。 下面是我们将在代码示例中使用的运行时类定义。
idl
// GiftBox.idl
runtimeclass GiftBox
{GiftBox(GiftBox biggerBox); // You can place a box inside a bigger box.
}假设我们想要在一个较大的 GiftBox 内构造 GiftBox。
GiftBox bigBox{ ... };// These are *not* what you intended. Doing it in one of these two ways
// copies bigBoxs backing-object-pointer into smallBox.
// The result is that smallBox bigBox.GiftBox smallBox{ bigBox };
auto smallBox{ GiftBox(bigBox) };正确的做法是显式调用激活工厂。
GiftBox bigBox{ ... };// These two ways call the activation factory explicitly.GiftBox smallBox{winrt::get_activation_factoryGiftBox, IGiftBoxFactory().CreateInstance(bigBox) };
auto smallBox{winrt::get_activation_factoryGiftBox, IGiftBoxFactory().CreateInstance(bigBox) };如果在 Windows 运行时组件中实现此 API
无论你是自行创作该组件还是该组件来自供应商本部分均适用。
在应用程序项目中引用 Windows 运行时组件的 Windows 运行时元数据 (.winmd) 文件然后生成。 在生成过程中cppwinrt.exe 工具生成标准 C 库该库全面描述或投影该组件的 API 接口。 换言之生成的库包含该组件的投影类型。
然后与 Windows 命名空间类型一样你只需包含标头并通过其构造函数之一构造投影类型。 应用程序项目的启动代码注册运行时类然后投影类型的构造函数调用 RoActivateInstance 来激活引用组件中的运行时类。
#include winrt/ThermometerWRC.hstruct App : implementsApp, IFrameworkViewSource, IFrameworkView
{ThermometerWRC::Thermometer thermometer;...
};如果在使用的项目中实现 API
本部分中的代码示例摘自 XAML 控件绑定到 C/WinRT 属性主题。 请查看该主题了解更多详细信息、代码和演练其中该演练描述了使用在采用它的同一项目中实现的运行时类的情况。
通过 XAML UI 使用的类型必须为运行时类即使其位于与 XAML 相同的项目中。 在这种情况下从运行时类的 Windows 运行时元数据 (.winmd) 中生成一个投影类型。 同样你还可包含一个标头但随后可选择 C/WinRT 版本 1.0 或版本 2.0 方法来构造该运行时类的实例。 版本 1.0 方法使用 winrt::make版本 2.0 方法被称作统一构造。 让我们来逐一查看。 使用 winrt::make 进行构造
让我们从默认方法C/WinRT 版本 1.0开始因为最好至少要熟悉该模式。 通过其 std::nullptr_t 构造函数构造投影类型。 该构造函数不执行任何初始化所以你接下来必须通过 winrt::make 帮助程序函数向该实例分配一个值同时传递任何必要的构造函数参数。 在使用代码的同一项目中实现的运行时类无需进行注册且无需通过 Windows 运行时/COM 激活进行实例化。
有关完整演练请参阅 XAML 控件绑定到 C/WinRT 属性。 本部分显示了摘自该演练的内容。
// MainPage.idl
import BookstoreViewModel.idl;
namespace Bookstore
{runtimeclass MainPage : Windows.UI.Xaml.Controls.Page{BookstoreViewModel MainViewModel{ get; };}
}// MainPage.h
...
struct MainPage : MainPageTMainPage
{...private:Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
...// MainPage.cpp
...
#include BookstoreViewModel.hMainPage::MainPage()
{m_mainViewModel winrt::makeBookstore::implementation::BookstoreViewModel();//通过 winrt::make 帮助程序函数向该实例分配一个值同时传递任何必要的构造函数参数。
...
}统一构造
在 C/WinRT 版本 2.0 及更高版本中有一种优化的构造形式可供你使用它被称作“统一构造”请参见 C/WinRT 2.0 中的新增功能和更改。
有关完整演练请参阅 XAML 控件绑定到 C/WinRT 属性。 本部分显示了摘自该演练的内容。
若要使用统一构造而不是 winrt::make你需要一个激活工厂。 要生成激活工厂一种好的方式是向 IDL 添加构造函数。
// MainPage.idl
import BookstoreViewModel.idl;
namespace Bookstore
{runtimeclass MainPage : Windows.UI.Xaml.Controls.Page{MainPage();BookstoreViewModel MainViewModel{ get; };}
}然后在 MainPage.h 中声明和初始化 m_mainViewModel此操作只需一步如下所示。
// MainPage.h
...
struct MainPage : MainPageTMainPage
{...private:Bookstore::BookstoreViewModel m_mainViewModel;...};
}
...接下来在 MainPage.cpp 中的 MainPage 构造函数中无需使用代码 m_mainViewModel winrt::makeBookstore::implementation::BookstoreViewModel();。
有关统一构造的详细信息请参阅选择加入统一构造和直接实现访问。 实例化和返回投影类型和接口
以下投影类型和实例的示例可能类似于使用的项目。 请记住投影类型如此示例中的一种投影类型是工具生成的而不是你自己创作的内容。
struct MyRuntimeClass : MyProject::IMyRuntimeClass, impl::requireMyRuntimeClass,Windows::Foundation::IStringable, Windows::Foundation::IClosableMyRuntimeClass 是投影类型投影接口包括 IMyRuntimeClass 、IStringable 和 IClosable 。 本主题已展示你可用来实例化投影类型的不同方法。 以下是提醒和总结这里使用 MyRuntimeClass 作为示例。
// The runtime class is implemented in another compilation unit (its either a Windows API,
// or its implemented in a second- or third-party component).
MyProject::MyRuntimeClass myrc1;// The runtime class is implemented in the same compilation unit.
MyProject::MyRuntimeClass myrc2{ nullptr };
myrc2 winrt::makeMyProject::implementation::MyRuntimeClass();你可以访问一个投影类型的所有接口的成员。可以将投影类型返回到调用方。投影类型和接口派生自 winrt::Windows::Foundation::IUnknown 。 因此你可以对投影类型或接口调用 IUnknown::as 以查询其他也可使用或返回到调用方的投影接口。 as 成员函数的工作方式类似于 QueryInterface 。
void f(MyProject::MyRuntimeClass const myrc)
{myrc.ToString();myrc.Close();IClosable iclosable myrc.asIClosable();iclosable.Close();
}激活工厂
创建 C/WinRT 对象的便利直接的方式如下所示。
using namespace winrt::Windows::Globalization::NumberFormatting;
...
CurrencyFormatter currency{ LUSD };不过有时你可能想要自己创建激活工厂然后在方便时从其创建对象。
下面的一些示例向你展示了如何使用 winrt::get_activation_factory 函数模板来实现此目的。
using namespace winrt::Windows::Globalization::NumberFormatting;
...
auto factory winrt::get_activation_factoryCurrencyFormatter, ICurrencyFormatterFactory();
CurrencyFormatter currency factory.CreateCurrencyFormatterCode(LUSD);using namespace winrt::Windows::Foundation;
...
auto factory winrt::get_activation_factoryUri, IUriRuntimeClassFactory();
Uri uri factory.CreateUri(Lhttp://www.contoso.com);上面两个示例中的类是来自 Windows 命名空间的类型。 在接下来的示例中ThermometerWRC::Thermometer 是在 Windows 运行时组件中实现的自定义类型。
auto factory winrt::get_activation_factoryThermometerWRC::Thermometer();
ThermometerWRC::Thermometer thermometer factory.ActivateInstanceThermometerWRC::Thermometer();成员/类型多义性
当成员函数的名称与类型的名称相同时会产生多义性。 根据 C 的在成员函数中进行非限定名称查找的规则必须先搜索类然后才能在命名空间中进行搜索。 “替换失败不是错误 (SFINAE)”规则不适用在对函数模板进行重载解析时适用。 因此如果类中的名称没有意义则编译器不会继续查找更好的匹配它会直接报告一个错误。
struct MyPage : Page
{void DoWork(){// This doesnt compile. You get the error// winrt::Windows::Foundation::IUnknown::as:// no matching overloaded function found.auto style{ Application::Current().Resources().Lookup(LMyStyle).asStyle() };}
}在上面的代码中编译器认为你是在将 FrameworkElement.Style()这在 C/WinRT 中是成员函数作为模板参数传递给 IUnknown::as。 解决方案是将名称 Style 强制解释为类型 Windows::UI::Xaml::Style。
struct MyPage : Page
{void DoWork(){// One option is to fully-qualify it.auto style{ Application::Current().Resources().Lookup(LMyStyle).asWindows::UI::Xaml::Style() };// Another is to force it to be interpreted as a struct name.auto style{ Application::Current().Resources().Lookup(LMyStyle).asstruct Style() };// If you have using namespace Windows::UI;, then this is sufficient.auto style{ Application::Current().Resources().Lookup(LMyStyle).asXaml::Style() };// Or you can force it to be resolved in the global namespace (into which// you imported the Windows::UI::Xaml namespace when you did// using namespace Windows::UI::Xaml;.auto style Application::Current().Resources().Lookup(LMyStyle).as::Style();}
}非限定名称查找有一个特殊的例外即名称后面跟有 ::在这种情况下它会忽略函数、变量和枚举值。 这样你就可以执行如下所示的代码。
struct MyPage : Page
{void DoSomething(){Visibility(Visibility::Collapsed); // No ambiguity here (special exception).}
}对 Visibility() 的调用会解析为 UIElement.Visibility 成员函数名称。 但是参数 Visibility::Collapsed 在 Visibility 一词后面跟有 ::因此系统会忽略方法名称编译器会查找枚举类。