Odpovědi na otázky, které budete pravděpodobně potřebovat k vytváření a využívání rozhraní API prostředí Windows Runtime pomocí C++/WinRT.
Důležité
Poznámky k verzi týkající se C++/WinRT najdete v článku Novinky a změny v C++/WinRT 2.0.
Note
Pokud máte dotaz na chybovou zprávu, kterou jste viděli, podívejte se také na téma Řešení potíží s C++/WinRT .
Kde najdu ukázkové aplikace C++/WinRT?
Jak můžu změnit cílení projektu C++/WinRT na novější verzi sady Windows SDK?
Proč můj nový projekt nejde zkompilovat po přechodu na C++/WinRT 2.0?
Úplnou sadu změn (včetně zásadních změn) najdete v článku Novinky a změny v C++/WinRT 2.0. Pokud například používáte rozsah založený for na kolekci prostředí Windows Runtime, budete teď muset #include <winrt/Windows.Foundation.Collections.h>.
Proč se můj nový projekt nezkompiluje? Používám Visual Studio 2017 (verze 15.8.0 nebo novější) a sadu SDK verze 17134
Pokud používáte Visual Studio 2017 (verze 15.8.0 nebo vyšší) a cílíte na Windows SDK verze 10.0.17134.0 (Windows 10, verze 1803), pak nově vytvořený projekt C++/WinRT může selhat kompilace s chybou "chyba C3861: "from_abi": identifikátor nebyl nalezen" a s jinými chybami pocházejícími ze base.h. Řešení je buď cílit na novější (konformnější) verzi sady Windows SDK, nebo nastavit vlastnost projektu C/C++>Language>Conformance mode: Ne (také pokud se /permissive- zobrazí ve vlastnosti projektu C/C++>Příkazový řádek v části Další možnosti a pak ho odstraňte).
Jak vyřešit chybu sestavení C++/WinRT VSIX už neposkytuje podporu sestavení projektu. Přidejte odkaz na projekt do balíčku NuGet Microsoft.Windows.CppWinRT"?
Nainstalujte do svého projektu balíček NuGet Microsoft.Windows.CppWinRT. Podrobnosti najdete v předchozích verzích rozšíření VSIX.
Jak přizpůsobím podporu sestavení v balíčku NuGet?
Podpora sestavení C++/WinRT (props/targets) je zdokumentována v readme balíčku NuGet Microsoft.Windows.CppWinRT.
Jaké jsou požadavky na rozšíření C++/WinRT Visual Studio (VSIX)?
Informace o verzi 1.0.190128.4 rozšíření VSIX a novějších najdete v tématu Visual Studio podpora C++/WinRT. Další verze najdete v předchozích verzích rozšíření VSIX.
Co je třída runtime?
Třída runtime je typ, který lze aktivovat a používat prostřednictvím moderních rozhraní COM, typicky napříč hranicemi spustitelných souborů. Třídu modulu runtime je však možné použít také v rámci kompilační jednotky, která ji implementuje. Deklarujete třídu modulu runtime v jazyce IDL (Interface Definition Language) a můžete ji implementovat ve standardním jazyce C++ pomocí C++/WinRT.
Co znamená projektovaný typ a typ implementace?
Pokud používáte pouze třídu prostředí Windows Runtime (třídu runtime), budete se zabývat výhradně projektovanými typy. C++/WinRT je projekce jazyka, takže projektované typy jsou součástí povrchu prostředí Windows Runtime, která se projektuje do C++ pomocí C++/WinRT. Další podrobnosti najdete v tématu Využívání rozhraní API s C++/WinRT.
Typ implementace obsahuje implementaci třídy runtime, takže je k dispozici pouze v projektu, který implementuje třídu runtime. Při práci v projektu, který implementuje třídy modulu runtime (projekt komponenty prostředí Windows Runtime nebo projekt, který používá uživatelské rozhraní XAML), je důležité, aby byl pohodlný s rozlišováním mezi typem implementace třídy modulu runtime a projektovaným typem, který představuje třídu modulu runtime projectovanou do C++/WinRT. Další informace najdete v článku Vytváření rozhraní API pomocí C++/WinRT.
Musím v IDL deklarovat konstruktor své třídy runtime?
Pouze pokud je třída modulu runtime navržená tak, aby byla spotřebována mimo implementaci kompilační jednotky (jedná se o komponentu prostředí Windows Runtime určenou pro obecnou spotřebu klientskými aplikacemi prostředí Windows Runtime). Úplné podrobnosti o účelu a důsledky deklarování konstruktorů v IDL naleznete v tématu Konstruktory třídy runtime.
Proč mi kompilátor dává chybu "C3779: consume_Something: funkce, která vrací "auto" nelze použít dříve, než je definována?
Používáte objekt prostředí Windows Runtime, aniž byste nejprve zahrnuli odpovídající hlavičkový soubor jmenného prostoru. Přidejte hlavičku pojmenovanou podle oboru názvů rozhraní API a projekt znovu sestavte. Další informace najdete v hlavičkových souborech projekce C++/WinRT.
Proč mi linker dává chybu "LNK2019: Nevyřešený externí symbol"?
Pokud je nevyřešený symbol samostatná funkce prostředí Windows Runtime, například RoInitialize, budete muset ve svém projektu explicitně připojit zastřešující knihovnu WindowsApp.lib. Projekce C++/WinRT závisí na některých z těchto bezplatných (nečlenových) funkcí a vstupních bodů. Pokud pro svou aplikaci použijete některou z šablon projektů C++/WinRT Visual Studio Extension (VSIX), pak se pro vás WindowsApp.lib automaticky nalinkuje. V opačném případě můžete k jeho zahrnutí použít nastavení odkazu projektu nebo to provést ve zdrojovém kódu.
#pragma comment(lib, "windowsapp")
Je důležité vyřešit všechny chyby linkeru, které lze vyřešit propojením s WindowsApp.lib namísto alternativní staticky linkované knihovny, jinak vaše aplikace neprojde testy aplikace pro Windows Certification Kit, které Visual Studio a Microsoft Store používají k ověřování odesílaných aplikací (to znamená, že vaši aplikaci následně nebude možné úspěšně přijmout do Microsoft Storu).
Pokud je nevyřešený symbol konstruktorem, možná jste zapomněli připojit hlavičkový soubor jmenného prostoru pro konstruovanou třídu. Přidejte hlavičkový soubor pojmenovaný podle oboru názvů třídy a znovu sestavte. Další informace najdete v hlavičkách projekce C++/WinRT.
Proč se mi zobrazuje výjimka „Třída není zaregistrována“?
V tomto případě je příznakem, že při vytváření třídy modulu runtime nebo přístupu ke statickému členu se zobrazí výjimka vyvolaná za běhu s hodnotou HRESULT REGDB_E_CLASSNOTREGISTERED.
Jednou z příčin může být to, že komponentu prostředí Windows Runtime nelze načíst. Ujistěte se, že soubor metadat komponenty prostředí Windows Runtime (.winmd) má stejný název jako binární soubor komponenty (the .dll), což je také název projektu a název kořenového oboru názvů. Také se ujistěte, že metadata prostředí Windows Runtime a binární soubor byly během procesu sestavení správně zkopírovány do složky Appx aplikace, která je používá. A ověřte, že aplikace, která komponentu využívá, AppxManifest.xml (také ve složce Appx) obsahuje element <InProcessServer>, který správně deklaruje aktivovatelnou třídu a název binárního souboru.
Jednotná konstrukce K této chybě může dojít také v případě, že se pokusíte vytvořit instanci místně implementované třídy modulu runtime prostřednictvím konstruktorů projektovaného typu (kromě jeho std::nullptr_t konstruktoru). K tomu budete potřebovat funkci C++/WinRT 2.0, která se často označuje jako jednotná konstrukce. Pokud se chcete k této funkci přihlásit, přečtěte si další informace a příklady kódu, viz Vyjádření souhlasu s jednotnou konstrukcí a přímý přístup k implementaci.
Způsob vytvoření instance místně implementovaných tříd modulu runtime, které nevyžadují jednotnou konstrukci, najdete v tématu Ovládací prvky XAML; vazba na vlastnost C++/WinRT.
Mám implementovat Windows::Foundation::IClosable a pokud ano, jak?
Pokud máte běhovou třídu, která ve svém destruktoru uvolňuje prostředky, a tato běhová třída je navržena tak, aby byla používána mimo kompilační jednotku, ve které je implementována (jde o komponentu prostředí Windows Runtime určenou k obecnému použití klientskými aplikacemi prostředí Windows Runtime), doporučujeme implementovat také IClosable, aby bylo podporováno používání této běhové třídy v jazycích, které nemají deterministickou finalizaci. Ujistěte se, že vaše prostředky budou uvolněny bez ohledu na to, zda je volán destruktor, IClosable::Close, nebo obojí. IClosable::Close může být volán libovolný početkrát.
Musím volat IClosable::Close u tříd modulu runtime, které používám?
IClosable existuje pro podporu jazyků, které nemají deterministické finalizace. Obecně tedy nemusíte volat IClosable::Close z C++/WinRT. Zvažte ale tyto výjimky tohoto obecného pravidla.
- Existují velmi vzácné případy, kdy dochází k souběhům při vypínání nebo k částečným deadlockům a kdy je nutné zavolat IClosable::Close. Pokud například používáte typy Windows.UI.Composition, můžete narazit na případy, kdy chcete objekty uvolnit v určitém pořadí, místo toho, aby tuto práci za vás provedlo zničení obálky C++/WinRT.
- Pokud nemůžete zaručit, že máte poslední zbývající odkaz na objekt (protože jste ho předali jiným rozhraním API, která by mohla uchovávat odkaz), je vhodné volat IClosable::Close .
- V případě pochybností je bezpečné volat IClosable::Close ručně, a nečekat, až obálka zavolá na zničení.
Pokud tedy víte, že máte poslední referenci, můžete nechat destruktor obálky udělat tu práci. Pokud potřebujete zavřít dříve, než zmizí poslední reference, musíte zavolat Close. Aby byl kód bezpečný vůči výjimkám, měli byste Close zapouzdřit do typu RAII (resource-acquisition-is-initialization), aby k uzavření došlo při odvíjení zásobníku. C++/WinRT nemá obálku unique_close , ale můžete si vytvořit vlastní.
Můžu použít LLVM/Clang ke kompilaci pomocí C++/WinRT?
Nepodporujeme sadu nástrojů LLVM a Clang pro C++/WinRT, ale interně ji používáme k ověření shody standardů C++/WinRT. Pokud byste například chtěli interně emulovat to, co děláme interně, můžete vyzkoušet experiment, například experiment popsaný níže.
Přejděte na stránku pro stažení LLVM, vyhledejte soubor Download LLVM 6.0.0>Předem připravené binární soubory a stáhněte si Clang pro Windows (64bitová verze). Během instalace se můžete rozhodnout přidat LLVM do systémové proměnné PATH, abyste ho mohli vyvolat z příkazového řádku. Pro účely tohoto experimentu můžete ignorovat všechny chyby typu "Nepodařilo se najít adresář sad nástrojů MSBuild" nebo chyby "Instalace integrace MSVC selhala", pokud se zobrazí. Existují různé způsoby volání LLVM/Clang; Následující příklad ukazuje jen jeden způsob.
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
Vzhledem k tomu, že C++/WinRT používá funkce ze standardu C++17, budete muset použít jakékoli příznaky kompilátoru potřebné k získání této podpory; tyto příznaky se liší od jednoho kompilátoru k jinému.
Visual Studio je vývojový nástroj, který podporujeme a doporučujeme pro C++/WinRT. Viz podpora Visual Studio pro C++/WinRT.
Proč nemá generovaná implementační funkce pro vlastnost jen pro čtení kvalifikátor const?
Když v MIDL 3.0 deklarujete vlastnost jen pro čtení, můžete očekávat, že nástroj cppwinrt.exe pro vás vygeneruje implementační funkci s kvalifikátorem const (funkce const zachází s ukazatelem this jako s const).
Určitě doporučujeme používat const všude, kde je to možné, ale cppwinrt.exe samotný nástroj se nepokouší zdůvodnět, které funkce implementace můžou být cont, a které nemusí. Můžete se rozhodnout označit libovolnou ze svých implementačních funkcí jako const, jak ukazuje tento příklad.
struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
winrt::hstring ToString() const
{
return L"MyStringable";
}
};
Tento kvalifikátor na const můžete odebrat, pokud se rozhodnete, že v jeho implementaci potřebujete změnit nějaký stav objektu. Ale u každé z vašich členských funkcí udělejte buď const, nebo non-const, ne oba. Jinými slovy, nepřetěžujte implementační funkci na const.
Kromě implementačních funkcí je dalším místem, kde přichází ke slovu const, projekce funkcí prostředí prostředí Windows Runtime. Zvažte tento kód.
int main()
{
winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
auto result{ s.ToString() };
}
Pro volání ToString výše příkaz Přejít na deklaraci v Visual Studio ukazuje, že projekce prostředí Windows Runtime IStringable::ToString do C++/WinRT vypadá takto.
winrt::hstring ToString() const;
Funkce v projekci jsou const bez ohledu na to, jak se rozhodnete kvalifikovat jejich implementaci. Na pozadí projekce volá binární rozhraní aplikace (ABI), což v podstatě představuje volání prostřednictvím ukazatele na rozhraní COM. Jediným stavem, se kterým promítaná metoda ToString pracuje, je ukazatel na rozhraní COM; a ten zjevně není potřeba měnit, takže funkce je const. To vám dává jistotu, že se na referenci IStringable, přes kterou voláte, nic nezmění, a zajišťuje, že můžete volat ToString i přes konstantní referenci na IStringable.
Mějte na paměti, že tyto příklady const jsou implementačními detaily projekcí a implementací C++/WinRT; představují určitou formu hygieny kódu ve váš prospěch. V COM ani v ABI prostředí Windows Runtime (pro členské funkce) nic takového jako const neexistuje.
Máte nějaká doporučení ke snížení velikosti kódu pro binární soubory C++/WinRT?
Při práci s prostředí Windows Runtime objekty byste se měli vyhnout vzoru kódování uvedenému níže, protože může mít negativní dopad na vaši aplikaci tím, že způsobí více binárního kódu, než je nutné vygenerovat.
anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();
V prostředí Windows Runtime světě nemůže kompilátor ukládat do mezipaměti hodnotu c() nebo rozhraní pro každou metodu, která je volána prostřednictvím nepřímých metod ('.'). Pokud nezasáhnete, výsledkem bude větší počet virtuálních volání a režijních nákladů. Výše uvedený vzor by mohl snadno vygenerovat dvakrát tolik kódu, kolik je nezbytně potřeba. Místo toho preferujte vzor zobrazený níže všude, kde je to možné. Generuje mnohem méně kódu a může také výrazně zlepšit výkon doby běhu.
auto a{ anobject.b().c() };
a.d();
a.e();
a.f();
Doporučený vzor uvedený výše platí nejen pro C++/WinRT, ale i pro všechny projekce jazyka prostředí Windows Runtime.
Jak změním řetězec na typ (například pro navigaci)?
Na konci příkladu kódu navigačního zobrazení (který je většinou v jazyce C#) je fragment kódu C++/WinRT, který ukazuje, jak to udělat.
Jak vyřeším nejednoznačnosti pomocí funkce GetCurrentTime nebo TRY?
Hlavičkový soubor winrt/Windows.UI.Xaml.Media.Animation.h deklaruje metodu s názvem GetCurrentTime, zatímco windows.h (prostřednictvím winbase.h) definuje makro s názvem GetCurrentTime. Když se tyto dvě koladují, kompilátor C++ vytvoří chybu C4002: Příliš mnoho argumentů pro vyvolání makra podobného funkci GetCurrentTime.
Podobně deklaruje metodu winrt/Windows.Globalization.h, zatímco definuje makro s názvem afx.h. Když tyto kolidují, kompilátor C++ vytvoří chybu C2334: neočekávané tokeny předcházející {; přeskočení zjevného těla funkce".
Pokud chcete odstranit jeden nebo oba problémy, můžete to udělat.
#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")
Jak zrychlit načítání symbolů?
V aplikaci Visual Studio, Nástroje>Možnosti>Ladění>Symboly> zaškrtněte Načíst pouze zadané moduly. Potom můžete v seznamu stacku kliknout pravým tlačítkem myši na soubory DLL a načíst jednotlivé moduly.
Note
Pokud toto téma neodpovídalo na vaši otázku, může vám pomoct navštívit komunitu vývojářů jazyka C++ Visual Studio nebo pomocí c++-winrt značky ve službě Stack Overflow.