Menggunakan API dengan C++/WinRT

Topik ini menunjukkan cara menggunakan API C++/WinRT , baik itu bagian dari Windows, yang diimplementasikan oleh vendor komponen pihak ketiga, atau diimplementasikan sendiri.

Penting

Sehingga contoh kode dalam topik ini singkat, dan mudah dicoba, Anda dapat mereproduksinya dengan membuat proyek Aplikasi Konsol Windows (C++/WinRT) baru, dan kode copy-pasting. Namun, Anda tidak dapat mengonsumsi jenis Windows Runtime kustom arbitrer (pihak ketiga) dari aplikasi yang tidak dikemas seperti itu. Anda hanya dapat menggunakan jenis Windows seperti itu.

Untuk menggunakan jenis Windows Runtime kustom (pihak ketiga) dari aplikasi konsol, Anda harus memberi aplikasi identitas paket sehingga dapat menyelesaikan pendaftaran jenis kustom yang digunakan. Untuk informasi selengkapnya, lihat Proyek Pengemasan Aplikasi Windows.

Atau, buat proyek baru dari templat proyek Blank App (C++/WinRT), Core App (C++/WinRT), atau Windows Runtime Component (C++/WinRT). Jenis aplikasi tersebut sudah memiliki identitas paket.

Jika API berada di namespace Windows

Ini adalah kasus paling umum di mana Anda akan menggunakan Windows Runtime API. Untuk setiap jenis dalam namespace Layanan Windows yang ditentukan dalam metadata, C++/WinRT mendefinisikan setara C++-friendly (disebut jenis yang diproyeksikan). Jenis yang diproyeksikan memiliki nama yang sepenuhnya memenuhi syarat yang sama dengan jenis Windows, tetapi ditempatkan di namespace C++ winrt menggunakan sintaks C++. Misalnya, Windows::Foundation::Uri diproyeksikan ke dalam C++/WinRT sebagai winrt::Windows::Foundation::Uri.

Berikut adalah contoh kode sederhana. Jika Anda ingin menyalin-tempel contoh kode berikut langsung ke file kode sumber utama proyek Aplikasi Konsol Windows (C++/WinRT), maka pertama-tama atur Tidak Menggunakan Header yang Telah Dikompilasi di properti proyek.

// main.cpp
#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    winrt::init_apartment();
    Uri contosoUri{ L"http://www.contoso.com" };
    Uri combinedUri = contosoUri.CombineUri(L"products");
}

Header winrt/Windows.Foundation.h yang disertakan adalah bagian dari SDK, ditemukan di dalam folder %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt\. Header dalam folder tersebut berisi jenis namespace Layanan Windows yang diproyeksikan ke dalam C++/WinRT. Dalam contoh ini, winrt/Windows.Foundation.h berisi winrt::Windows::Foundation::Uri, yang merupakan jenis yang diproyeksikan untuk kelas runtime Windows::Foundation::Uri.

Tip

Setiap kali Anda ingin menggunakan jenis dari namespace Layanan Windows, sertakan header C++/WinRT yang sesuai dengan namespace layanan tersebut. Direktif using namespace bersifat opsional, tetapi nyaman.

Dalam contoh kode di atas, setelah menginisialisasi C++/WinRT, kami mengalokasikan nilai winrt::Windows::Foundation::Uri jenis yang diproyeksikan melalui salah satu konstruktor yang didokumentasikan secara publik (Uri(String), dalam contoh ini). Untuk ini, kasus penggunaan yang paling umum, itu biasanya yang harus Anda lakukan. Setelah Anda memiliki nilai jenis yang diproyeksikan C++/WinRT, Anda dapat memperlakukannya seolah-olah itu adalah instans dari jenis Windows Runtime yang sebenarnya, karena memiliki semua anggota yang sama.

Bahkan, nilai yang diproyeksikan adalah proksi; ini pada dasarnya hanya penunjuk cerdas ke objek cadangan. Konstruktor nilai yang diproyeksikan memanggil RoActivateInstance untuk membuat instans kelas Windows Runtime yang mendukung (Windows.Foundation.Uri, dalam hal ini), dan menyimpan antarmuka default objek tersebut di dalam nilai proyeksi baru. Seperti yang diilustrasikan di bawah ini, panggilan Anda ke anggota nilai yang diproyeksikan benar-benar mendelegasikan, melalui penunjuk cerdas, ke objek backing; di mana perubahan status terjadi.

Jenis Windows::Foundation::Uri yang diproyeksikan

Ketika nilai berada di contosoUri luar cakupan, nilai tersebut akan dihancurkan, dan merilis referensinya ke antarmuka default. Jika referensi tersebut adalah referensi terakhir ke objek Windows Runtime Windows.Foundation.Uri yang mendukung, objek cadangan juga akan dihancurkan.

Tip

Jenis yang diproyeksikan adalah pembungkus melalui jenis Windows Runtime untuk tujuan mengonsumsi API-nya. Misalnya, antarmuka yang diproyeksikan adalah pembungkus melalui antarmuka Windows Runtime.

Header proyeksi C++/WinRT

Untuk menggunakan API namespace Windows dari C++/WinRT, Anda menyertakan header dari %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt folder . Anda harus menyertakan header yang sesuai dengan setiap namespace yang Anda gunakan.

Misalnya, untuk windows::Security::Cryptography::Certificates namespace, definisi jenis C++/WinRT yang setara berada di winrt/Windows.Security.Cryptography.Certificates.h. Termasuk header tersebut memberi Anda akses ke semua jenis di namespace Windows::Security::Cryptography::Certificates .

Terkadang, satu header namespace akan menyertakan bagian header namespace terkait, tetapi Anda tidak boleh mengandalkan detail implementasi ini. Sertakan header secara eksplisit untuk namespace yang Anda gunakan.

Misalnya, metode Certificate::GetCertificateBlob mengembalikan antarmuka Windows::Storage::Aliran::IBuffer. Sebelum memanggil metode Certificate::GetCertificateBlob, Anda harus menyertakan winrt/Windows.Storage.Streams.h file header namespace untuk memastikan bahwa Anda dapat menerima dan mengoperasikan pada Windows::Storage::Aliran::IBuffer yang dikembalikan.

Lupa menyertakan header namespace yang diperlukan sebelum menggunakan jenis di namespace tersebut adalah sumber umum kesalahan build.

Mengakses anggota melalui objek, melalui antarmuka, atau melalui ABI

Dengan proyeksi C++/WinRT, representasi runtime kelas Windows Runtime tidak lebih dari antarmuka ABI yang mendasarinya. Namun, demi kenyamanan Anda, Anda dapat membuat kode terhadap kelas dengan cara yang diinginkan penulis mereka. Misalnya, Anda dapat memanggil metode ToString dari Uri seolah-olah itu adalah metode kelas (pada kenyataannya, di bawah sampul, itu adalah metode pada antarmuka IStringable terpisah).

WINRT_ASSERT adalah definisi makro, dan meluas ke _ASSERTE.

Uri contosoUri{ L"http://www.contoso.com" };
WINRT_ASSERT(contosoUri.ToString() == L"http://www.contoso.com/"); // QueryInterface is called at this point.

Kenyamanan ini dicapai melalui kueri untuk antarmuka yang sesuai. Tapi kau selalu memegang kendali. Anda dapat memilih untuk memberikan sedikit kenyamanan untuk sedikit performa dengan mengambil antarmuka IStringable sendiri dan menggunakannya secara langsung. Dalam contoh kode di bawah ini, Anda mendapatkan penunjuk antarmuka IStringable aktual pada waktu proses (melalui kueri satu kali). Setelah itu, panggilan Anda ke ToString langsung, dan menghindari panggilan lebih lanjut ke QueryInterface.

...
IStringable stringable = contosoUri; // One-off QueryInterface.
WINRT_ASSERT(stringable.ToString() == L"http://www.contoso.com/");

Anda dapat memilih teknik ini jika Anda tahu bahwa Anda akan memanggil beberapa metode pada antarmuka yang sama.

Kebetulan, jika Anda ingin mengakses anggota di tingkat ABI maka Anda dapat. Contoh kode di bawah ini menunjukkan caranya, dan ada detail lebih lanjut dan contoh kode di Interop antara C++/WinRT dan 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{ L"http://www.contoso.com" };

    int port{ contosoUri.Port() }; // Access the Port "property" accessor via C++/WinRT.

    winrt::com_ptr<ABI::Windows::Foundation::IUriRuntimeClass> abiUri{
        contosoUri.as<ABI::Windows::Foundation::IUriRuntimeClass>() };
    HRESULT hr = abiUri->get_Port(&port); // Access the get_Port ABI function.
}

Inisialisasi tertunda

Di C++/WinRT, setiap jenis yang diproyeksikan memiliki konstruktor C++/WinRT std::nullptr_t khusus. Dengan pengecualian yang satu itu, semua konstruktor jenis yang diproyeksikan—termasuk konstruktor default—menyebabkan objek Windows Runtime yang mendukung dibuat, dan memberi Anda penunjuk cerdas untuk itu. Jadi, aturan itu berlaku di mana saja konstruktor default digunakan, seperti variabel lokal yang tidak diinisialisasi, variabel global yang tidak diinisialisasi, dan variabel anggota yang tidak diinisialisasi.

Jika, di sisi lain, Anda ingin membuat variabel dari jenis yang diproyeksikan tanpa itu pada gilirannya membangun objek Windows Runtime yang mendukung (sehingga Anda dapat menunda pekerjaan itu sampai nanti), maka Anda dapat melakukannya. Deklarasikan variabel atau bidang Anda menggunakan konstruktor khusus C++/WinRT std::nullptr_t (yang disuntikkan proyeksi C++/WinRT ke setiap kelas runtime). Kami menggunakan konstruktor khusus tersebut dengan m_gamerPicBuffer dalam contoh kode di bawah ini.

#include <winrt/Windows.Storage.Streams.h>
using namespace winrt::Windows::Storage::Streams;

#define MAX_IMAGE_SIZE 1024

struct Sample
{
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer m_gamerPicBuffer{ nullptr };
};

int main()
{
    winrt::init_apartment();
    Sample s;
    // ...
    s.DelayedInit();
}

Semua konstruktor pada jenis yang diproyeksikan kecualikonstruktor std::nullptr_t menyebabkan objek Windows Runtime cadangan dibuat. Konstruktor std::nullptr_t pada dasarnya adalah no-op. Ini mengharapkan objek yang diproyeksikan untuk diinisialisasi pada waktu berikutnya. Jadi, apakah kelas runtime memiliki konstruktor default atau tidak, Anda dapat menggunakan teknik ini untuk inisialisasi tertunda yang efisien.

Pertimbangan ini memengaruhi tempat lain di mana Anda memanggil konstruktor default, seperti di vektor dan peta. Pertimbangkan contoh kode ini, di mana Anda memerlukan proyek Aplikasi Kosong (C++/WinRT).

std::map<int, TextBlock> lookup;
lookup[2] = value;

Penugasan membuat TextBlock baru, lalu segera menimpanya dengan value. Berikut obatnya.

std::map<int, TextBlock> lookup;
lookup.insert_or_assign(2, value);

Lihat juga Bagaimana konstruktor default memengaruhi koleksi.

Jangan menunda-inisialisasi secara tidak sengaja

Berhati-hatilah agar Anda tidak memanggil konstruktor std::nullptr_t secara tidak sengaja. Resolusi konflik kompilator mendukungnya atas konstruktor pabrik. Misalnya, pertimbangkan dua definisi kelas runtime ini.

// GiftBox.idl
runtimeclass GiftBox
{
    GiftBox();
}

// Gift.idl
runtimeclass Gift
{
    Gift(GiftBox giftBox); // You can create a gift inside a box.
}

Katakanlah kita ingin membuat Hadiah yang tidak ada di dalam kotak (Hadiah yang dibangun dengan GiftBox yang tidak diinisialisasi). Pertama, mari kita lihat cara yang salah untuk melakukan itu. Kita tahu bahwa ada konstruktor Hadiah yang mengambil GiftBox. Tetapi jika kita tergoda untuk melewati GiftBox null (memanggil konstruktor Hadiah melalui inisialisasi seragam, seperti yang kita lakukan di bawah), maka kita tidak akan mendapatkan hasil yang kita inginkan.

// These are *not* what you intended. Doing it in one of these two ways
// actually *doesn't* create the intended backing Windows Runtime Gift object;
// only an empty smart pointer.

Gift gift{ nullptr };
auto gift{ Gift(nullptr) };

Apa yang Anda dapatkan di sini adalah Hadiah yang tidak diinisialisasi. Anda tidak mendapatkan Hadiah dengan GiftBox yang tidak diinisialisasi. Berikut adalah cara yang benar untuk melakukan itu.

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

Dalam contoh yang salah, meneruskan nullptr penyelesaian harfiah mendukung konstruktor yang menginisialisasi penundaan. Untuk mengatasi mendukung konstruktor pabrik, jenis parameter harus berupa GiftBox. Anda masih memiliki opsi untuk meneruskan GiftBox yang ditunda secara eksplisit, seperti yang ditunjukkan dalam contoh yang benar.

Contoh berikutnya ini juga benar, karena parameter memiliki jenis GiftBox, dan bukan std::nullptr_t.

GiftBox giftBox{ nullptr };
Gift gift{ giftBox }; // Calls factory constructor.

Ini hanya ketika Anda lulus nullptr harfiah bahwa ambiguitas muncul.

Jangan menyalin konstruksi secara tidak sengaja.

Perhatian ini mirip dengan yang dijelaskan di bagian Jangan tunda-inisialisasi dengan kesalahan di atas.

Selain konstruktor yang menginisialisasi penundaan, proyeksi C++/WinRT juga menyuntikkan konstruktor salinan ke setiap kelas runtime. Ini adalah konstruktor parameter tunggal yang menerima jenis yang sama dengan objek yang sedang dibangun. Penunjuk cerdas yang dihasilkan menunjuk ke objek Windows Runtime yang mendukung yang sama seperti yang ditunjukkan oleh parameter konstruktornya. Hasilnya adalah dua objek penunjuk cerdas yang menunjuk ke objek penunjuk yang sama.

Berikut adalah definisi kelas runtime yang akan kita gunakan dalam contoh kode.

// GiftBox.idl
runtimeclass GiftBox
{
    GiftBox(GiftBox biggerBox); // You can place a box inside a bigger box.
}

Katakanlah kita ingin membuat GiftBox di dalam GiftBox yang lebih besar.

GiftBox bigBox{ ... };

// These are *not* what you intended. Doing it in one of these two ways
// copies bigBox's backing-object-pointer into smallBox.
// The result is that smallBox == bigBox.

GiftBox smallBox{ bigBox };
auto smallBox{ GiftBox(bigBox) };

Cara yang benar untuk melakukannya adalah dengan memanggil pabrik aktivasi secara eksplisit.

GiftBox bigBox{ ... };

// These two ways call the activation factory explicitly.

GiftBox smallBox{
    winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
auto smallBox{
    winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };

Jika API diimplementasikan dalam komponen Windows Runtime

Bagian ini menerapkan apakah Anda menulis komponen sendiri, atau berasal dari vendor.

Catatan

Untuk informasi tentang menginstal dan menggunakan C++/WinRT Visual Studio Extension (VSIX) dan paket NuGet (yang bersama-sama menyediakan templat proyek dan dukungan build), lihat Dukungan Visual Studio untuk C++/WinRT.

Dalam proyek aplikasi Anda, referensikan file metadata Windows Runtime komponen Windows Runtime (.winmd), dan build. Selama build, alat ini cppwinrt.exe menghasilkan pustaka C++ standar yang sepenuhnya menjelaskan—atau proyek—permukaan API untuk komponen. Dengan kata lain, pustaka yang dihasilkan berisi jenis yang diproyeksikan untuk komponen.

Kemudian, sama seperti untuk jenis namespace Layanan Windows, Anda menyertakan header dan membuat jenis yang diproyeksikan melalui salah satu konstruktornya. Kode startup proyek aplikasi Anda mendaftarkan kelas runtime, dan konstruktor jenis yang diproyeksikan memanggil RoActivateInstance untuk mengaktifkan kelas runtime dari komponen yang direferensikan.

#include <winrt/ThermometerWRC.h>

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer thermometer;
    ...
};

Untuk detail selengkapnya, kode, dan panduan penggunaan API yang diterapkan dalam komponen Windows Runtime, lihat komponen Windows Runtime dengan peristiwa C++/WinRT dan Penulis di C++/WinRT.

Jika API diimplementasikan dalam proyek yang menggunakan

Contoh kode di bagian ini diambil dari topik kontrol XAML; ikat ke properti C++/WinRT. Lihat topik tersebut untuk detail, kode, dan panduan selengkapnya tentang mengonsumsi kelas runtime yang diterapkan dalam proyek yang sama yang mengonsumsinya.

Jenis yang dikonsumsi dari UI XAML harus merupakan kelas runtime, bahkan jika berada dalam proyek yang sama dengan XAML. Untuk skenario ini, Anda menghasilkan jenis yang diproyeksikan dari metadata Windows Runtime kelas runtime (.winmd). Sekali lagi, Anda menyertakan header, tetapi kemudian Anda memiliki pilihan antara cara C++/WinRT versi 1.0 atau versi 2.0 untuk membangun instans kelas runtime. Metode versi 1.0 menggunakan winrt::make; metode versi 2.0 dikenal sebagai konstruksi seragam. Mari kita lihat masing-masing secara bergantian.

Membangun dengan menggunakan winrt::make

Mari kita mulai dengan metode default (C++/WinRT versi 1.0), karena ada baiknya setidaknya terbiasa dengan pola tersebut. Anda membuat jenis yang diproyeksikan melalui konstruktor std::nullptr_t . Konstruktor tersebut tidak melakukan inisialisasi apa pun, jadi Anda selanjutnya harus menetapkan nilai ke instans melalui fungsi winrt::make helper, meneruskan argumen konstruktor yang diperlukan. Kelas runtime yang diterapkan dalam proyek yang sama dengan kode konsumsi tidak perlu didaftarkan, atau dibuat melalui aktivasi Windows Runtime/COM.

Lihat kontrol XAML; ikat ke properti C++/WinRT untuk panduan lengkap. Bagian ini menunjukkan ekstrak dari panduan tersebut.

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        BookstoreViewModel MainViewModel{ get; };
    }
}

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
    ...
    private:
        Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
...

// MainPage.cpp
...
#include "BookstoreViewModel.h"

MainPage::MainPage()
{
    m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
    ...
}

Konstruksi seragam

Dengan C++/WinRT versi 2.0 dan yang lebih baru, ada bentuk konstruksi yang dioptimalkan yang tersedia untuk Anda yang dikenal sebagai konstruksi seragam (lihat Berita, dan perubahan, di C++/WinRT 2.0).

Lihat kontrol XAML; ikat ke properti C++/WinRT untuk panduan lengkap. Bagian ini menunjukkan ekstrak dari panduan tersebut.

Untuk menggunakan konstruksi seragam alih-alih winrt::make, Anda memerlukan pabrik aktivasi. Cara yang baik untuk menghasilkan adalah dengan menambahkan konstruktor ke IDL Anda.

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

Kemudian, dalam mendeklarasikan dan menginisialisasi m_mainViewModel hanya dalam satu langkah, seperti yang ditunjukkan MainPage.h di bawah ini.

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
    ...
    private:
        Bookstore::BookstoreViewModel m_mainViewModel;
        ...
    };
}
...

Dan kemudian, di konstruktor MainPage di MainPage.cpp, tidak perlu kode m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();.

Untuk informasi selengkapnya tentang konstruksi seragam, dan contoh kode, lihat Ikut serta dalam konstruksi seragam, dan akses implementasi langsung.

Membuat instans dan mengembalikan jenis dan antarmuka yang diproyeksikan

Berikut adalah contoh tampilan jenis dan antarmuka yang diproyeksikan dalam proyek konsumsi Anda. Ingatlah bahwa jenis yang diproyeksikan (seperti yang dalam contoh ini), dihasilkan alat, dan bukan sesuatu yang akan Anda tulis sendiri.

struct MyRuntimeClass : MyProject::IMyRuntimeClass, impl::require<MyRuntimeClass,
    Windows::Foundation::IStringable, Windows::Foundation::IClosable>

MyRuntimeClass adalah jenis yang diproyeksikan; antarmuka yang diproyeksikan termasuk IMyRuntimeClass, IStringable, dan IClosable. Topik ini telah menunjukkan berbagai cara di mana Anda dapat membuat instans jenis yang diproyeksikan. Berikut adalah pengingat dan ringkasan, menggunakan MyRuntimeClass sebagai contoh.

// The runtime class is implemented in another compilation unit (it's either a Windows API,
// or it's 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::make<MyProject::implementation::MyRuntimeClass>();
  • Anda dapat mengakses anggota semua antarmuka jenis yang diproyeksikan.
  • Anda dapat mengembalikan jenis yang diproyeksikan ke pemanggil.
  • Jenis dan antarmuka yang diproyeksikan berasal dari winrt::Windows::Foundation::IUnknown. Jadi, Anda dapat memanggil IUnknown::seperti pada jenis atau antarmuka yang diproyeksikan untuk mengkueri antarmuka lain yang diproyeksikan, yang juga dapat Anda gunakan atau kembali ke pemanggil. Fungsi sebagai anggota berfungsi seperti QueryInterface.
void f(MyProject::MyRuntimeClass const& myrc)
{
    myrc.ToString();
    myrc.Close();
    IClosable iclosable = myrc.as<IClosable>();
    iclosable.Close();
}

Pabrik aktivasi

Cara langsung yang nyaman untuk membuat objek C++/WinRT adalah sebagai berikut.

using namespace winrt::Windows::Globalization::NumberFormatting;
...
CurrencyFormatter currency{ L"USD" };

Tetapi mungkin ada kalanya Anda ingin membuat pabrik aktivasi sendiri, lalu membuat objek darinya sesuai keinginan Anda. Berikut adalah beberapa contoh yang menunjukkan caranya, menggunakan templat fungsi winrt::get_activation_factory .

using namespace winrt::Windows::Globalization::NumberFormatting;
...
auto factory = winrt::get_activation_factory<CurrencyFormatter, ICurrencyFormatterFactory>();
CurrencyFormatter currency = factory.CreateCurrencyFormatterCode(L"USD");
using namespace winrt::Windows::Foundation;
...
auto factory = winrt::get_activation_factory<Uri, IUriRuntimeClassFactory>();
Uri uri = factory.CreateUri(L"http://www.contoso.com");

Kelas dalam dua contoh di atas adalah jenis dari namespace Windows. Dalam contoh berikutnya ini, ThermometerWRC::Thermometer adalah jenis kustom yang diimplementasikan dalam komponen Windows Runtime.

auto factory = winrt::get_activation_factory<ThermometerWRC::Thermometer>();
ThermometerWRC::Thermometer thermometer = factory.ActivateInstance<ThermometerWRC::Thermometer>();

Ambiguitas Anggota/Jenis

Ketika fungsi anggota memiliki nama yang sama dengan jenis, ada ambiguitas. Aturan untuk pencarian nama C++ yang tidak memenuhi syarat dalam fungsi anggota menyebabkannya mencari kelas sebelum mencari di namespace layanan. Kegagalan substitusi bukan aturan kesalahan (SFINAE) tidak berlaku (berlaku selama resolusi kelebihan beban templat fungsi). Jadi jika nama di dalam kelas tidak masuk akal, pengkompilasi tidak terus mencari kecocokan yang lebih baik—itu hanya melaporkan kesalahan.

struct MyPage : Page
{
    void DoWork()
    {
        // This doesn't compile. You get the error
        // "'winrt::Windows::Foundation::IUnknown::as':
        // no matching overloaded function found".
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Style>() };
    }
}

Di atas, pengompilasi berpikir bahwa Anda melewati FrameworkElement.Style() (yang, di C++/WinRT, adalah fungsi anggota) sebagai parameter templat ke IUnknown::as. Solusinya adalah memaksa nama Style untuk ditafsirkan sebagai jenis Windows::UI::Xaml::Style.

struct MyPage : Page
{
    void DoWork()
    {
        // One option is to fully-qualify it.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Windows::UI::Xaml::Style>() };

        // Another is to force it to be interpreted as a struct name.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<struct Style>() };

        // If you have "using namespace Windows::UI;", then this is sufficient.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Xaml::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(L"MyStyle").as<::Style>();
    }
}

Pencarian nama yang tidak memenuhi syarat memiliki pengecualian khusus jika nama diikuti oleh ::, dalam hal ini mengabaikan fungsi, variabel, dan nilai enum. Ini memungkinkan Anda untuk melakukan hal-hal seperti ini.

struct MyPage : Page
{
    void DoSomething()
    {
        Visibility(Visibility::Collapsed); // No ambiguity here (special exception).
    }
}

Panggilan untuk Visibility() menyelesaikan ke nama fungsi anggota UIElement.Visibility. Tetapi parameter Visibility::Collapsed mengikuti kata Visibility dengan ::, sehingga nama metode diabaikan, dan pengkompilasi menemukan kelas enum.

API penting