Tanya jawab umum tentang C++/WinRT

Jawaban atas pertanyaan yang mungkin Anda miliki tentang penulisan dan penggunaan API Windows Runtime dengan C++/WinRT.

Important

Untuk catatan rilis tentang C++/WinRT, lihat Berita, dan perubahan, di C++/WinRT 2.0.

Note

Jika pertanyaan Anda adalah tentang pesan kesalahan yang telah Anda lihat, lihat juga topik Pemecahan Masalah C++/WinRT .

Di mana saya dapat menemukan aplikasi sampel C++/WinRT?

Bagaimana cara menargetkan ulang proyek C++/WinRT saya ke versi Windows SDK yang lebih baru?

Mengapa proyek baru saya tidak dikompilasi, sekarang saya telah pindah ke C++/WinRT 2.0?

Untuk daftar lengkap perubahan (termasuk perubahan yang menyebabkan inkompatibilitas), lihat Berita dan perubahan di C++/WinRT 2.0. Misalnya, jika Anda menggunakan for berbasis rentang pada koleksi Windows Runtime, kini Anda perlu #include <winrt/Windows.Foundation.Collections.h>.

Mengapa proyek baru saya tidak dikompilasi? Saya menggunakan Visual Studio 2017 (versi 15.8.0 atau lebih tinggi), dan SDK versi 17134

Jika Anda menggunakan Visual Studio 2017 (versi 15.8.0 atau lebih tinggi), dan menargetkan SDK Windows versi 10.0.17134.0 (Windows 10, versi 1803), maka proyek C++/WinRT yang baru dibuat mungkin gagal dikompilasi dengan kesalahan "kesalahan C3861: 'from_abi': pengidentifikasi tidak ditemukan", dan dengan kesalahan lain yang berasal dari base.h. Solusinya adalah menargetkan versi yang lebih baru (lebih sesuai) dari Windows SDK, atau mengatur properti proyek C/C++>Mode Kesuaian>: Tidak (juga, jika /permisif- muncul di properti proyek C/C++>Command Line di bawah Opsi Tambahan, lalu hapus).

Bagaimana cara mengatasi kesalahan build "C++/WinRT VSIX tidak lagi menyediakan dukungan build untuk proyek. Harap tambahkan referensi proyek ke paket NuGet Microsoft.Windows.CppWinRT.

Instal paket NuGet Microsoft.Windows.CppWinRT ke proyek Anda. Untuk detailnya, lihat Versi ekstensi VSIX yang lebih lama.

Bagaimana cara menyesuaikan dukungan build dalam paket NuGet?

Dukungan build C++/WinRT (props/targets) didokumentasikan dalam readme paket NuGet Microsoft.Windows.CppWinRT.

Apa saja persyaratan untuk C++/WinRT Visual Studio Extension (VSIX)?

Untuk ekstensi VSIX versi 1.0.190128.4 dan yang lebih baru, lihat dukungan Visual Studio untuk C++/WinRT. Untuk versi lain, lihat Versi ekstensi VSIX yang lebih lama.

Apa itu kelas runtime?

Kelas runtime adalah sebuah jenis yang dapat diaktifkan dan digunakan melalui antarmuka COM modern, biasanya lintas batas antareksekusi. Namun, kelas runtime juga dapat digunakan dalam unit kompilasi yang mengimplementasikannya. Anda mendeklarasikan kelas runtime dalam Bahasa Definisi Antarmuka (IDL), dan Anda dapat menerapkannya di C++ standar menggunakan C++/WinRT.

Apa arti jenis proyeksi dan jenis implementasi?

Jika Anda hanya menggunakan kelas Windows Runtime (kelas runtime), maka Anda hanya akan bekerja dengan tipe yang diproyeksikan. C++/WinRT adalah proyeksi bahasa, jadi jenis yang diproyeksikan adalah bagian dari permukaan Windows Runtime yang diproyeksikan ke C++ dengan C++/WinRT. Untuk detail selengkapnya, lihat Menggunakan API dengan C++/WinRT.

Jenis implementasi berisi implementasi kelas runtime, sehingga hanya tersedia dalam proyek yang mengimplementasikan kelas runtime. Saat Anda bekerja dalam proyek yang mengimplementasikan kelas runtime (proyek komponen Windows Runtime, atau proyek yang menggunakan XAML UI), penting untuk nyaman dengan perbedaan antara jenis implementasi Anda untuk kelas runtime, dan jenis yang diproyeksikan yang mewakili kelas runtime yang diproyeksikan ke dalam C++/WinRT. Untuk detail selengkapnya, lihat API Penulis dengan C++/WinRT.

Apakah saya perlu mendeklarasikan konstruktor di IDL kelas runtime saya?

Hanya jika kelas runtime dirancang untuk digunakan dari luar unit kompilasi penerapannya (ini adalah komponen Windows Runtime yang ditujukan untuk konsumsi umum oleh aplikasi klien Windows Runtime). Untuk detail lengkap tentang tujuan dan konsekuensi mendeklarasikan konstruktor di IDL, lihat Konstruktor kelas runtime.

Mengapa kompilator menampilkan kesalahan "C3779: consume_Something: fungsi yang mengembalikan 'auto' tidak dapat digunakan sebelum didefinisikan"?

Anda menggunakan objek Windows Runtime tanpa terlebih dahulu menyertakan file header namespace yang sesuai. Sertakan file header yang dinamai sesuai dengan namespace API, lalu kompilasi ulang. Untuk informasi selengkapnya, lihat header proyeksi C++/WinRT.

Mengapa linker memberi saya kesalahan "LNK2019: Simbol eksternal yang tidak terselesaikan"?

Jika simbol yang belum terselesaikan adalah fungsi bebas Windows Runtime, seperti RoInitialize, maka Anda harus secara eksplisit menautkan pustaka payung WindowsApp.lib dalam proyek Anda. Proyeksi C++/WinRT tergantung pada beberapa fungsi gratis (non-anggota) dan titik masuk ini. Jika Anda menggunakan salah satu templat proyek C++/WinRT Visual Studio Extension (VSIX) untuk aplikasi Anda, maka WindowsApp.lib ditautkan untuk Anda secara otomatis. Jika tidak, Anda dapat menggunakan pengaturan tautan proyek untuk menyertakannya, atau melakukannya dalam kode sumber.

#pragma comment(lib, "windowsapp")

Penting bagi Anda untuk mengatasi setiap kesalahan linker yang dapat Anda atasi dengan menautkan WindowsApp.lib alih-alih pustaka penautan statis alternatif; jika tidak, aplikasi Anda tidak akan lulus pengujian Windows App Certification Kit yang digunakan oleh Visual Studio dan Microsoft Store untuk memvalidasi pengajuan, yang berarti aplikasi Anda tidak akan dapat berhasil diterima ke Microsoft Store.

Jika simbol yang belum terselesaikan adalah konstruktor, Anda mungkin lupa menyertakan file header namespace untuk kelas yang sedang dibangun. Sertakan file header untuk namespace kelas, lalu kompilasi ulang. Untuk informasi selengkapnya, lihat header proyeksi C++/WinRT.

Mengapa saya mendapatkan pengecualian "kelas tidak terdaftar"?

Dalam hal ini, gejalanya adalah bahwa—saat membangun kelas runtime atau mengakses anggota statis—Anda melihat pengecualian yang dilemparkan pada runtime dengan nilai HRESULT REGDB_E_CLASSNOTREGISTERED.

Salah satu penyebabnya adalah komponen Windows Runtime Anda tidak dapat dimuat. Pastikan bahwa file metadata Windows Runtime komponen (.winmd) memiliki nama yang sama dengan biner komponen (the .dll), yang juga merupakan nama proyek dan namespace akar. Pastikan juga bahwa metadata Windows Runtime dan biner telah disalin dengan benar oleh proses build ke folder Appx aplikasi yang menggunakan. Dan pastikan bahwa aplikasi pemanggil AppxManifest.xml (juga di folder Appx) memuat elemen <InProcessServer> yang dengan benar mendeklarasikan kelas yang dapat diaktifkan dan nama biner.

Konstruksi seragam Kesalahan ini juga dapat terjadi jika Anda mencoba membuat instans kelas runtime yang diimplementasikan secara lokal melalui salah satu konstruktor jenis yang diproyeksikan (selain konstruktor std::nullptr_t ). Untuk melakukannya, Anda memerlukan fitur C++/WinRT 2.0 yang sering disebut konstruksi seragam. Jika Anda ingin ikut serta dalam fitur tersebut, maka untuk informasi selengkapnya, dan contoh kode, lihat Ikut serta dalam konstruksi seragam, dan akses implementasi langsung.

Untuk cara membuat instans kelas runtime yang diterapkan secara lokal yang tidak memerlukan konstruksi seragam, lihat kontrol XAML; ikat ke properti C++/WinRT.

Haruskah saya menerapkan Windows::Foundation::IClosable dan, jika demikian, bagaimana?

Jika Anda memiliki kelas runtime yang membebaskan sumber daya dalam destruktornya, dan kelas runtime tersebut dirancang untuk digunakan dari luar unit kompilasi penerapannya (ini adalah komponen Windows Runtime yang ditujukan untuk konsumsi umum oleh aplikasi klien Windows Runtime), maka kami sarankan Anda juga menerapkan IClosable untuk mendukung konsumsi kelas runtime Anda berdasarkan bahasa yang tidak memiliki finalisasi deterministik. Pastikan sumber daya Anda dibebaskan baik saat destruktor, IClosable::Close, maupun keduanya dipanggil. IClosable::Close dapat dipanggil berapa kali pun.

Apakah saya perlu memanggil IClosable::Close pada kelas runtime yang saya gunakan?

IClosable ada untuk mendukung bahasa yang tidak memiliki finalisasi deterministik. Jadi, secara umum, Anda tidak perlu memanggil IClosable::Close from C++/WinRT. Tetapi pertimbangkan pengecualian ini untuk aturan umum tersebut.

  • Ada kasus yang sangat jarang terjadi yang melibatkan ras shutdown atau pelukan semi-mematikan, di mana Anda perlu memanggil IClosable::Close. Jika Anda menggunakan tipe Windows.UI.Composition, misalnya, Anda mungkin akan menemui situasi ketika Anda ingin melepaskan objek dalam urutan tertentu, alih-alih membiarkan penghancuran pembungkus C++/WinRT menanganinya untuk Anda.
  • Jika Anda tidak dapat menjamin bahwa Anda memiliki referensi terakhir yang tersisa ke objek (karena Anda meneruskannya ke API lain, yang dapat menyimpan referensi), maka memanggil IClosable::Close adalah ide yang baik.
  • Jika ragu, sebaiknya panggil IClosable::Close secara manual, daripada menunggu wrapper memanggilnya saat dihancurkan.

Jadi, jika Anda tahu bahwa Anda memiliki referensi terakhir, maka Anda dapat membiarkan destruktor pembungkus melakukan pekerjaan. Jika Anda perlu menutup sebelum referensi terakhir lenyap, maka Anda perlu memanggil Tutup. Agar aman pengecualian, Anda harus Menutup dalam jenis resource-acquisition-is-initialization (RAII) (sehingga penutupan terjadi pada unwind). C++/WinRT tidak memiliki pembungkus unique_close , tetapi Anda dapat membuat sendiri.

Dapatkah saya menggunakan LLVM/Clang untuk mengkompilasi dengan C++/WinRT?

Kami tidak mendukung toolchain LLVM dan Clang untuk C++/WinRT, tetapi kami menggunakannya secara internal untuk memvalidasi kesamaan standar C++/WinRT. Misalnya, jika Anda ingin meniru apa yang kami lakukan secara internal, maka Anda dapat mencoba eksperimen seperti yang dijelaskan di bawah ini.

Buka Halaman Unduhan LLVM, cari Unduh Biner Bawaan LLVM 6.0.0>, dan unduh Clang untuk Windows (64-bit). Selama penginstalan, pilih untuk menambahkan LLVM ke variabel sistem PATH sehingga Anda akan dapat memanggilnya dari prompt perintah. Untuk tujuan eksperimen ini, Anda dapat mengabaikan kesalahan "Gagal menemukan direktori toolsets MSBuild" dan/atau "Penginstalan integrasi MSVC gagal", jika Anda melihatnya. Ada berbagai cara untuk memanggil LLVM/Clang; contoh di bawah ini hanya menunjukkan satu cara.

C:\ExperimentWithLLVMClang>type main.cpp
// main.cpp
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "ole32")

#include <winrt/Windows.Foundation.h>
#include <stdio.h>
#include <iostream>

using namespace winrt;

int main()
{
    winrt::init_apartment();
    Windows::Foundation::Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    std::wcout << rssFeedUri.Domain().c_str() << std::endl;
}

C:\ExperimentWithLLVMClang>clang-cl main.cpp /EHsc /I ..\.. -Xclang -std=c++17 -Xclang -Wno-delete-non-virtual-dtor -o app.exe

C:\ExperimentWithLLVMClang>app
windows.com

Karena C++/WinRT menggunakan fitur dari standar C++17, Anda harus menggunakan bendera kompilator apa pun yang diperlukan untuk mendapatkan dukungan tersebut; bendera tersebut berbeda dari satu pengkompilasi ke pengkompilasi lainnya.

Visual Studio adalah alat pengembangan yang kami dukung dan rekomendasikan untuk C++/WinRT. Lihat dukungan Visual Studio untuk C++/WinRT.

Mengapa fungsi implementasi yang dihasilkan untuk properti baca-saja tidak memiliki kualifikasi const?

Ketika Anda mendeklarasikan properti hanya-baca di MIDL 3.0, Anda mungkin mengharapkan alat cppwinrt.exe untuk menghasilkan fungsi implementasi bagi Anda yang diberi kualifikasi const (fungsi const memperlakukan pointer this-nya sebagai const).

Kami tentu merekomendasikan penggunaan const sedapat mungkin, tetapi alat cppwinrt.exe itu sendiri tidak mencoba menilai fungsi implementasi mana yang mungkin dapat berupa const, dan mana yang mungkin tidak. Anda dapat memilih untuk membuat salah satu fungsi implementasi Anda menjadi const, seperti dalam contoh ini.

struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
    winrt::hstring ToString() const
    {
        return L"MyStringable";
    }
};

Anda dapat menghapus const kualifikasi tersebut di ToString jika Anda memutuskan bahwa Anda perlu mengubah beberapa status objek dalam implementasinya. Tetapi, buat setiap fungsi anggota hanya berupa const atau non-const, bukan keduanya. Dengan kata lain, jangan membebani fungsi implementasi secara berlebihan pada const.

Selain fungsi implementasi Anda, tempat lain di mana const masuk ke dalam gambar berada dalam proyeksi fungsi Windows Runtime. Pertimbangkan kode ini.

int main()
{
    winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
    auto result{ s.ToString() };
}

Untuk panggilan ke ToString di atas, perintah Buka Deklarasi di Visual Studio menunjukkan bahwa proyeksi Windows Runtime IStringable::ToString ke C++/WinRT terlihat seperti ini.

winrt::hstring ToString() const;

Fungsi pada proyeksi bersifat const, apa pun qualifier yang Anda pilih untuk implementasinya. Di balik layar, mekanisme proyeksi memanggil application binary interface (ABI), yang pada dasarnya merupakan pemanggilan melalui pointer antarmuka COM. Satu-satunya keadaan yang digunakan oleh ToString hasil proyeksi adalah penunjuk antarmuka COM tersebut; dan tentu saja fungsi itu tidak perlu memodifikasi penunjuk tersebut, sehingga fungsi tersebut bersifat const. Ini memberi Anda jaminan bahwa itu tidak akan mengubah apa pun tentang referensi IStringable yang Anda panggil, dan memastikan bahwa Anda dapat memanggil ToString bahkan dengan referensi const ke IStringable.

Pahami bahwa contoh-contoh const ini adalah detail implementasi proyeksi dan implementasi C++/WinRT; ini merupakan kebersihan kode untuk keuntungan Anda. Tidak ada hal seperti const pada COM atau Windows Runtime ABI (untuk fungsi anggota).

Apakah Anda memiliki rekomendasi untuk mengurangi ukuran kode untuk biner C++/WinRT?

Saat bekerja dengan objek Windows Runtime, Anda harus menghindari pola pengodean yang ditunjukkan di bawah ini karena dapat berdampak negatif pada aplikasi Anda dengan menyebabkan lebih banyak kode biner daripada yang diperlukan untuk dihasilkan.

anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();

Di dunia Windows Runtime, pengkompilasi tidak dapat menyimpan dalam cache nilai c() maupun antarmuka untuk setiap metode yang dipanggil melalui indireksi ('.'). Kecuali Anda campur tangan, hal itu mengakibatkan lebih banyak pemanggilan virtual dan overhead penghitungan referensi. Pola di atas dapat dengan mudah menghasilkan kode dua kali lebih banyak daripada yang benar-benar diperlukan. Sebagai gantinya, lebih suka pola yang ditunjukkan di bawah ini di mana pun Anda bisa. Ini menghasilkan kode yang jauh lebih sedikit, dan juga dapat secara dramatis meningkatkan performa run time Anda.

auto a{ anobject.b().c() };
a.d();
a.e();
a.f();

Pola yang direkomendasikan yang ditunjukkan di atas tidak hanya berlaku untuk C++/WinRT tetapi untuk semua proyeksi bahasa Windows Runtime.

Bagaimana cara mengubah string menjadi jenis (untuk navigasi, misalnya)?

Di akhir contoh kode tampilan Navigasi (yang sebagian besar berada di C#), ada cuplikan kode C++/WinRT yang menunjukkan cara melakukan ini.

Bagaimana cara mengatasi ambiguitas dengan GetCurrentTime dan/atau TRY?

File winrt/Windows.UI.Xaml.Media.Animation.h header mendeklarasikan metode bernama GetCurrentTime, sedangkan windows.h (melalui winbase.h) mendefinisikan makro bernama GetCurrentTime. Ketika keduanya bertabrakan, pengkompilasi C++ menghasilkan "kesalahan C4002: Terlalu banyak argumen untuk pemanggilan makro seperti fungsi GetCurrentTime".

Demikian pula, winrt/Windows.Globalization.h mendeklarasikan metode bernama TRY, sementara afx.h mendefinisikan makro bernama TRY. Ketika keduanya bertabrakan, kompiler C++ menghasilkan "kesalahan C2334: token tak terduga sebelum '{'; melewati isi fungsi yang tampak".

Untuk memperbaiki satu atau kedua masalah, Anda dapat melakukan ini.

#pragma push_macro("GetCurrentTime")
#pragma push_macro("TRY")
#undef GetCurrentTime
#undef TRY
#include <winrt/include_your_cppwinrt_headers_here.h>
#include <winrt/include_your_cppwinrt_headers_here.h>
#pragma pop_macro("TRY")
#pragma pop_macro("GetCurrentTime")

Bagaimana cara mempercepat pemuatan simbol?

Di Visual Studio, Opsi Alat>>Simbol>Debugging> centang Muat hanya modul yang ditentukan. Anda kemudian dapat mengklik kanan DLL di daftar tumpukan, dan memuat modul individual.

Note

Jika topik ini tidak menjawab pertanyaan Anda, maka Anda mungkin menemukan bantuan dengan mengunjungi komunitas pengembang Visual Studio C++, atau dengan menggunakan c++-winrt tag di Stack Overflow.