Partager via


Scénario 1 : Générer un fichier PRI à partir de ressources de type chaîne et de fichiers de ressources

Dans ce scénario, nous utiliserons les API d’indexation des ressources des packages (PRI) pour créer une nouvelle application représentant notre système de version personnalisé. L’objectif de ce système de version personnalisé, rappelez-vous, est de créer des fichiers PRI pour une application UWP cible. Dans le cadre de cette procédure-à-pas, nous allons donc créer des exemples de fichiers de ressources (contenant des applications et d’autres types de ressources) pour représenter les ressources de l’application UWP cible.

Nouveau projet

Commencez par créer un nouveau projet dans Microsoft Visual Studio. Créez un projet Visual C++ Windows Console Application et nommez-le CBSConsoleApp (pour "custom build system console app").

Choisissez x64 dans la liste déroulante Plateformes de solution.

En-têtes, bibliothèque statique et dll

Les API de la PRI sont déclarées dans le fichier d’en-tête MrmResourceIndexer.h (qui est installé dans %ProgramFiles(x86)%\Windows Kits\10\Include\<WindowsTargetPlatformVersion>\um\). Ouvrez le fichier CBSConsoleApp.cpp et incluez l’en-tête ainsi que d’autres en-têtes dont vous aurez besoin.

#include <string>
#include <windows.h>
#include <MrmResourceIndexer.h>

Les API sont mises en œuvre dans MrmSupport.dll, auquel vous accédez en établissant un lien avec la bibliothèque statique MrmSupport.lib. Ouvrez les propriétés de votre projet, cliquez sur Linker>Input, modifiez AdditionalDependencies et ajoutez MrmSupport.lib.

Construisez la solution, puis copiez MrmSupport.dll à partir de C:\Program Files (x86)\Windows Kits\10\bin\<WindowsTargetPlatformVersion>\x64\ dans votre dossier de sortie de la construction (probablement C:\Users\%USERNAME%\source\repos\CBSConsoleApp\x64\Debug\).

Ajoutez la fonction d’aide suivante à CBSConsoleApp.cpp, puisque nous en aurons besoin.

inline void ThrowIfFailed(HRESULT hr)
{
	if (FAILED(hr))
	{
		// Set a breakpoint on this line to catch Win32 API errors.
		throw new std::exception();
	}
}

Dans la fonction main(), ajoutez des appels pour initialiser et désinitialiser COM.

int main()
{
	::ThrowIfFailed(::CoInitializeEx(nullptr, COINIT_MULTITHREADED));
	
	// More code will go here.
	
	::CoUninitialize();
}

Resource appartenant à l’application UWP cible.

Nous allons maintenant avoir besoin de quelques exemples de fichiers de ressources (contenant des chaînes, et d’autres types de ressources) pour représenter les ressources de l’application UWP cible. Bien entendu, ces fichiers peuvent être situés n’importe où sur votre système de fichiers. Mais pour cette procédure-à-pasas, il sera plus pratique de les placer dans le dossier du projet de CBSConsoleApp afin que tout soit au même endroit. Vous n’avez besoin d’ajouter ces fichiers de ressources qu’au système de fichiers ; ne les ajoutez pas au projet CBSConsoleApp.

Dans le même dossier que celui qui contient CBSConsoleApp.vcxproj, ajoutez un nouveau sous-dossier nommé UWPAppProjectRootFolder. Dans ce nouveau sous-dossier, créez ces exemples de fichiers de ressources.

\UWPAppProjectRootFolder\sample-image.png

Ce fichier peut contenir n’importe quelle image PNG.

\UWPAppProjectRootFolder\resources.resw

<?xml version="1.0"?>
<root>
	<data name="LocalizedString1">
		<value>LocalizedString1-neutral</value>
	</data>
	<data name="LocalizedString2">
		<value>LocalizedString2-neutral</value>
	</data>
	<data name="NeutralOnlyString">
		<value>NeutralOnlyString-neutral</value>
	</data>
</root>

\UWPAppProjectRootFolder\de-DE\resources.resw

<?xml version="1.0"?>
<root>
	<data name="LocalizedString2">
		<value>LocalizedString2-de-DE</value>
	</data>
</root>

\UWPAppProjectRootFolder\en-US\resources.resw

<?xml version="1.0"?>
<root>
	<data name="LocalizedString1">
		<value>LocalizedString1-en-US</value>
	</data>
	<data name="EnOnlyString">
		<value>EnOnlyString-en-US</value>
	</data>
</root>

Indexer les ressources et créer un fichier PRI

Dans la fonction main(), avant l’appel à l’initialisation de COM, déclarez quelques chaînes dont nous aurons besoin et créez également le dossier de sortie dans lequel nous générerons notre fichier PRI.

std::wstring projectRootFolderUWPApp{ L"UWPAppProjectRootFolder" };
std::wstring generatedPRIsFolder{ projectRootFolderUWPApp + L"\\Generated PRIs" };
std::wstring filePathPRI{ generatedPRIsFolder + L"\\resources.pri" };
std::wstring filePathPRIDumpBasic{ generatedPRIsFolder + L"\\resources-pri-dump-basic.xml" };

::CreateDirectory(generatedPRIsFolder.c_str(), nullptr);

Immédiatement après l’appel à Initialize COM, déclarez un handle d’indexeur de ressources, puis appelez MrmCreateResourceIndexer pour créer un indexeur de ressources.

MrmResourceIndexerHandle indexer;
::ThrowIfFailed(::MrmCreateResourceIndexer(
	L"OurUWPApp",
	projectRootFolderUWPApp.c_str(),
	MrmPlatformVersion::MrmPlatformVersion_Windows10_0_0_0,
	L"language-en_scale-100_contrast-standard",
	&indexer));

Voici une explication des arguments transmis à MrmCreateResourceIndexer.

  • Le nom de famille du package de notre application UWP cible, qui sera utilisé comme nom de la carte de ressources lorsque nous générerons ultérieurement un fichier PRI à partir de cet indexeur de ressources.
  • La racine du projet de notre application UWP cible. En d’autres termes, le chemin d’accès à nos fichiers de ressources. Nous le spécifions pour pouvoir ensuite spécifier des chemins relatifs à cette racine dans les appels d’API ultérieurs au même indexeur de ressources.
  • La version de Windows que nous voulons cibler.
  • Une liste de qualificateurs de ressources par défaut.
  • Un pointeur sur le gestionnaire de notre indexeur de ressources afin que la fonction puisse le définir.

L’étape suivante consiste à ajouter nos ressources à l’indexeur de ressources que nous venons de créer. resources.resw est un fichier de ressources (.resw) qui contient les applications neutres pour notre application UWP cible. Faites défiler vers le haut (dans cette rubrique) si vous voulez voir son contenu. de-DE\resources.resw contient nos chaînes allemandes et en-US\resources.resw nos chaînes anglaises. Pour ajouter les chaînes de caractères contenues dans un fichier de ressources à un indexeur de ressources, vous appelez MrmIndexResourceContainerAutoQualifiers. Troisièmement, nous appelons la fonction MrmIndexFile pour ajouter un fichier contenant une ressource image neutre à l’indexeur de ressources.

::ThrowIfFailed(::MrmIndexResourceContainerAutoQualifiers(indexer, L"resources.resw"));
::ThrowIfFailed(::MrmIndexResourceContainerAutoQualifiers(indexer, L"de-DE\\resources.resw"));
::ThrowIfFailed(::MrmIndexResourceContainerAutoQualifiers(indexer, L"en-US\\resources.resw"));
::ThrowIfFailed(::MrmIndexFile(indexer, L"ms-resource:///Files/sample-image.png", L"sample-image.png", L""));

Dans l’appel à MrmIndexFile, la valeur L "ms-resource:///Files/sample-image.png" est l’uri de la ressource. Le premier segment du chemin d’accès est « Files », et c’est ce qui sera utilisé comme nom de sous-arbre de la carte des ressources lorsque nous générerons ultérieurement un fichier PRI à partir de cet indexeur de ressources.

Après avoir informé l’indexeur de ressources de nos fichiers de ressources, il est temps de lui demander de générer un fichier PRI sur le disque en appelant la fonction MrmCreateResourceFile.

::ThrowIfFailed(::MrmCreateResourceFile(indexer, MrmPackagingModeStandaloneFile, MrmPackagingOptionsNone, generatedPRIsFolder.c_str()));

A ce stade, un fichier PRI nommé resources.pri a été créé dans un dossier nommé Generated PRIs. Maintenant que nous en avons terminé avec l’indexeur de ressources, nous appelons MrmDestroyIndexerAndMessages pour détruire son handle et libérer toutes les ressources machine qu’il a allouées.

::ThrowIfFailed(::MrmDestroyIndexerAndMessages(indexer));

Étant donné qu’un fichier PRI est binaire, il sera plus facile de visualiser ce que nous venons de générer si nous vidons le fichier PRI binaire en son équivalent XML. C’est ce que fait l’appel à MrmDumpPriFile.

::ThrowIfFailed(::MrmDumpPriFile(filePathPRI.c_str(), nullptr, MrmDumpType::MrmDumpType_Basic, filePathPRIDumpBasic.c_str()));

Voici une explication des arguments transmis à MrmDumpPriFile.

  • Le chemin d’accès au fichier PRI à extraire. Nous n’utilisons pas l’indexeur de ressources dans cet appel (nous venons de le détruire), nous devons donc spécifier un chemin d’accès complet.
  • Pas de fichier de schéma. Nous verrons ce qu’est un schéma plus loin dans la rubrique.
  • Juste les informations de base.
  • Le chemin d’un fichier XML à créer.

C’est ce que contient le fichier PRI, qui est ici converti en XML.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PriInfo>
	<ResourceMap name="OurUWPApp" version="1.0" primary="true">
		<Qualifiers>
			<Language>en-US,de-DE</Language>
		</Qualifiers>
		<ResourceMapSubtree name="Files">
			<NamedResource name="sample-image.png" uri="ms-resource://OurUWPApp/Files/sample-image.png">
				<Candidate type="Path">
					<Value>sample-image.png</Value>
				</Candidate>
			</NamedResource>
		</ResourceMapSubtree>
		<ResourceMapSubtree name="resources">
			<NamedResource name="EnOnlyString" uri="ms-resource://OurUWPApp/resources/EnOnlyString">
				<Candidate qualifiers="Language-en-US" isDefault="true" type="String">
					<Value>EnOnlyString-en-US</Value>
				</Candidate>
			</NamedResource>
			<NamedResource name="LocalizedString1" uri="ms-resource://OurUWPApp/resources/LocalizedString1">
				<Candidate qualifiers="Language-en-US" isDefault="true" type="String">
					<Value>LocalizedString1-en-US</Value>
				</Candidate>
				<Candidate type="String">
					<Value>LocalizedString1-neutral</Value>
				</Candidate>
			</NamedResource>
			<NamedResource name="LocalizedString2" uri="ms-resource://OurUWPApp/resources/LocalizedString2">
				<Candidate qualifiers="Language-de-DE" type="String">
					<Value>LocalizedString2-de-DE</Value>
				</Candidate>
				<Candidate type="String">
					<Value>LocalizedString2-neutral</Value>
				</Candidate>
			</NamedResource>
			<NamedResource name="NeutralOnlyString" uri="ms-resource://OurUWPApp/resources/NeutralOnlyString">
				<Candidate type="String">
					<Value>NeutralOnlyString-neutral</Value>
				</Candidate>
			</NamedResource>
		</ResourceMapSubtree>
	</ResourceMap>
</PriInfo>

L’information commence par une carte de ressources, qui est nommée avec le nom de famille du package de notre application UWP cible. La carte des ressources contient deux sous-arbres : un pour les ressources de fichiers que nous avons indexées et un autre pour nos ressources de chaînes de caractères. Remarquez que le nom de la famille du package a été inséré dans tous les URI des ressources.

La première ressource de type chaîne est EnOnlyString de en-US\resources.resw, et elle n’a qu’un seul candidat (qui correspond au qualificatif language-en-US). Vient ensuite LocalizedString1 provenant à la fois de resources.resw et de en-US\resources.resw. Par conséquent, elle a deux candidats : un candidat correspondant à language-en-US et un candidat neutre de repli qui correspond à n’importe quel contexte. De même, LocalizedString2 a deux candidats : langue-de-DE et neutre. Enfin, NeutralOnlyString n’existe que sous forme neutre. Je lui ai donné ce nom pour qu’il soit clair qu’elle n’est pas destinée à être localisée.

Résumé

Dans ce scénario, nous avons montré comment utiliser les API d’indexation des ressources du package (PRI) pour créer un indexeur de ressources. Nous avons ajouté des ressources de chaînes et des fichiers de ressources à l’indexeur de ressources. Ensuite, nous avons utilisé l’indexeur de ressources pour générer un fichier PRI binaire. Enfin, nous avons extrait le fichier PRI binaire sous la forme d’un fichier XML afin de pouvoir confirmer qu’il contient les informations attendues.

API importantes