Tanya jawab umum tentang C++/WinRT

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

Penting

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

Catatan

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?

Lihat 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 kumpulan perubahan lengkap (termasuk perubahan yang melanggar), lihat Berita, dan perubahan, di C++/WinRT 2.0. Misalnya, jika Anda menggunakan rentang berdasarkan for koleksi Windows Runtime, maka Anda sekarang harus #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 yang lebih tinggi), dan menargetkan Windows SDK 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 Windows SDK yang lebih baru (lebih sesuai), atau mengatur properti proyek C/C++>mode Kesuaian Bahasa>: Tidak (juga, jika /permisif- muncul di properti proyek C/C++>Baris Perintah di bawah Opsi Tambahan, lalu hapus).

Bagaimana cara mengatasi kesalahan build "C++/WinRT VSIX tidak lagi menyediakan dukungan build proyek. Tambahkan referensi proyek ke paket Nuget Microsoft.Windows.CppWinRT"?

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

Bagaimana cara menyesuaikan dukungan build dalam paket NuGet?

Dukungan build C++/WinRT (alat peraga/target) didokumenkan 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 jenis yang dapat diaktifkan dan dikonsumsi melalui antarmuka COM modern, biasanya di seluruh batas yang dapat dieksekusi. 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 akan berurusan secara eksklusif dengan jenis 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 pengompilasi memberi saya kesalahan "C3779: consume_Something:: yang mengembalikan 'otomatis' tidak dapat digunakan sebelum ditentukan"?

Anda menggunakan objek Windows Runtime tanpa terlebih dahulu menyertakan file header namespace yang sesuai. Sertakan header bernama untuk namespace LAYANAN API, dan bangun 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 kesalahan linker apa pun yang Anda dapat dengan menautkan WindowsApp.lib alih-alih pustaka tautan statis alternatif, jika tidak, aplikasi Anda tidak akan lulus tes Windows App Certification Kit yang digunakan oleh Visual Studio dan oleh Microsoft Store untuk memvalidasi pengiriman (yang berarti bahwa aplikasi Anda tidak akan berhasil diserap ke Microsoft Store).

Jika simbol yang belum terselesaikan adalah konstruktor, Anda mungkin lupa menyertakan file header namespace untuk kelas yang sedang dibangun. Sertakan header bernama untuk namespace layanan kelas, dan bangun 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 ( .dll), yang juga merupakan nama proyek dan nama namespace layanan akar. Pastikan juga bahwa metadata Windows Runtime dan biner telah disalin secara inti oleh proses build ke folder aplikasi Appx yang menggunakan. Dan konfirmasikan bahwa aplikasi AppxManifest.xml yang mengonsumsi (juga di Appx folder) berisi <elemen InProcessServer> dengan benar yang 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 oleh bahasa yang tidak memiliki finalisasi deterministik. Pastikan sumber daya Anda dibebaskan apakah destruktor, IClosable::Close, atau keduanya dipanggil. IClosable::Close mungkin disebut berapa kali.

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 jenis Windows.UI.Composition , sebagai contoh, maka Anda mungkin mengalami kasus di mana Anda ingin membuang objek dalam urutan yang ditetapkan, sebagai alternatif untuk memungkinkan penghancuran pembungkus C++/WinRT melakukan pekerjaan 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, aman untuk memanggil IClosable::Tutup secara manual, daripada menunggu pembungkus menyebutnya sebagai penghancuran.

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 baca-saja di MIDL 3.0, Anda mungkin mengharapkan cppwinrt.exe alat untuk menghasilkan fungsi implementasi untuk Anda yang const-memenuhi syarat (fungsi const memperlakukan pointer ini sebagai const).

Kami tentu merekomendasikan penggunaan const sedapat mungkin, tetapi alat itu cppwinrt.exe sendiri tidak mencoba untuk beralasan tentang fungsi implementasi mana yang mungkin konsekuensinya, dan 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 anggota Anda berfungsi baik 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 adalah 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 adalah const tidak peduli bagaimana Anda memilih untuk memenuhi syarat implementasi Anda dari mereka. Di balik layar, proyeksi memanggil antarmuka biner aplikasi (ABI), yang berjumlah panggilan melalui penunjuk antarmuka COM. Satu-satunya status bahwa ToString yang diproyeksikan berinteraksi dengan adalah bahwa penunjuk antarmuka COM; dan tentu saja tidak perlu memodifikasi penunjuk itu, sehingga fungsinya adalah 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 nilai c() atau antarmuka untuk setiap metode yang dipanggil melalui tidak langsung ('.'). Kecuali Anda mengintervensi, itu menghasilkan lebih banyak panggilan virtual dan referensi yang menghitung overhead. 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 disarankan 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 ini bertabrakan, pengkompilasi C++ menghasilkan "kesalahan C2334: token tak terduga sebelumnya '{'; melompati isi fungsi yang jelas".

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>>Men-debug>Simbol> centang Muat hanya modul yang ditentukan. Anda kemudian dapat mengklik kanan DLL di daftar tumpukan, dan memuat modul individual.

Catatan

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.