Compartir a través de


Escenario 1: Generación de un archivo PRI a partir de recursos de cadena y archivos de recursos

En este escenario, usaremos las API de indexación de recursos de paquete (PRI) para crear una nueva aplicación para representar nuestro sistema de compilación personalizado. Recuerde que el propósito de este sistema de compilación personalizado es crear archivos PRI para una aplicación UWP de destino. Por lo tanto, como parte de este tutorial, crearemos algunos archivos de recursos de ejemplo (que contienen cadenas y otros tipos de recursos) para representar los recursos de la aplicación UWP de destino.

Nuevo proyecto

Para empezar, crea un proyecto en Microsoft Visual Studio. Cree un proyecto de Aplicación de consola de Windows de Visual C++ y asígnele el nombre CBSConsoleApp (para "aplicación de consola del sistema de compilación personalizado").

Elija x64 en la lista desplegable Plataformas de solución.

Encabezados, biblioteca estática y dll

Las API PRI se declaran en el archivo de encabezado MrmResourceIndexer.h (que se instala en %ProgramFiles(x86)%\Windows Kits\10\Include\<WindowsTargetPlatformVersion>\um\). Abra el archivo CBSConsoleApp.cpp e incluya el encabezado junto con otros encabezados que necesitará.

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

Las API se implementan en MrmSupport.dll, al que se accede mediante la vinculación a la biblioteca estática MrmSupport.lib. Abra las Propiedades del proyecto, haga clic en Vinculador>Entrada, edite AdditionalDependencies y agregue MrmSupport.lib.

Compile la solución y, a continuación, copie MrmSupport.dll desde C:\Program Files (x86)\Windows Kits\10\bin\<WindowsTargetPlatformVersion>\x64\ a la carpeta de salida de compilación (probablemente C:\Users\%USERNAME%\source\repos\CBSConsoleApp\x64\Debug\).

Agregue la siguiente función auxiliar a CBSConsoleApp.cpp, ya que la necesitaremos.

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

En la función main(), agregue llamadas para inicializar y desinicializar COM.

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

Archivos de recursos que pertenecen a la aplicación UWP de destino

Ahora necesitaremos algunos archivos de recursos de ejemplo (que contienen cadenas y otros tipos de recursos) para representar los recursos de la aplicación UWP de destino. Estos, por supuesto, se pueden ubicar en cualquier lugar del sistema de archivos. Pero para este tutorial será conveniente colocarlos en la carpeta del proyecto de CBSConsoleApp para que todo esté en un mismo lugar. Solo tiene que agregar estos archivos de recursos al sistema de archivos; no los agregue al proyecto CBSConsoleApp.

Dentro de la misma carpeta que contiene CBSConsoleApp.vcxproj, agregue una nueva subcarpeta denominada UWPAppProjectRootFolder. Dentro de esa nueva subcarpeta, cree estos archivos de recursos de ejemplo.

\UWPAppProjectRootFolder\sample-image.png

Este archivo puede contener cualquier imagen 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>

Indexación de los recursos y creación de un archivo PRI

En la función main(), antes de la llamada para inicializar COM, declare algunas cadenas que necesitaremos y cree también la carpeta de salida en la que se va a generar el archivo 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);

Inmediatamente después de la llamada a Inicializar COM, declare un identificador de indexador de recursos y, a continuación, llame a MrmCreateResourceIndexer para crear un indexador de recursos.

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

Esta es una explicación de los argumentos que se pasan a MrmCreateResourceIndexer.

  • El nombre de familia del paquete de nuestra aplicación UWP de destino, que se usará como nombre de la asignación de recursos cuando más adelante generemos un archivo PRI a partir de este indexador de recursos.
  • Raíz del proyecto de nuestra aplicación UWP de destino. En otras palabras, la ruta de acceso a nuestros archivos de recursos. Especificamos esto para que podamos especificar rutas de acceso relativas a esa raíz en llamadas API posteriores al mismo indexador de recursos.
  • Versión de Windows que queremos tener como destino.
  • Lista de calificadores de recursos predeterminados.
  • Puntero al identificador del indexador de recursos para que la función pueda establecerlo.

El siguiente paso es agregar nuestros recursos al indexador de recursos que acabamos de crear. resources.resw es un archivo de recursos (.resw) que contiene las cadenas neutrales para nuestra aplicación UWP de destino. Desplácese hacia arriba (en este tema) si desea ver su contenido. de-DE\resources.resw contiene las cadenas en alemán y en-US\resources.resw las cadenas en inglés. Para agregar los recursos de cadena de un archivo de recursos a un indexador de recursos, llame a MrmIndexResourceContainerAutoQualifiers. En tercer lugar, llamamos a la función MrmIndexFile en un archivo que contiene un recurso de imagen neutral al indexador de recursos.

::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""));

En la llamada a MrmIndexFile, el valor L"ms-resource:///Files/sample-image.png" es el URI del recurso. El primer segmento de ruta de acceso es "Files" y eso es lo que se usará como nombre del subárbol de la asignación de recursos cuando más adelante generemos un archivo PRI a partir de este indexador de recursos.

Después de haber informado al indexador de recursos sobre nuestros archivos de recursos, es el momento de que generemos un archivo PRI en el disco llamando a la función MrmCreateResourceFile function.

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

En este momento, se ha creado un archivo PRI denominado resources.pri dentro de una carpeta denominada Generated PRIs. Ahora que hemos terminado con el indexador de recursos, llamamos a MrmDestroyIndexerAndMessages para destruir su identificador y liberar los recursos de la máquina asignados.

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

Dado que un archivo PRI es binario, será más fácil ver lo que acabamos de generar si volcamos el archivo PRI binario en su equivalente XML. Una llamada a MrmDumpPriFile hace justamente eso.

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

Esta es una explicación de los argumentos que se pasan a MrmDumpPriFile.

  • Ruta de acceso al archivo PRI que se va a volcar. No usamos el indexador de recursos en esta llamada (lo acabamos de destruir), por lo que es necesario especificar una ruta de acceso de archivo completa.
  • No hay ningún archivo de esquema. Analizaremos qué es un esquema más adelante en este tema.
  • Solo la información básica.
  • La ruta de acceso de un archivo XML para crear.

Esto es lo que contiene el archivo PRI, volcado a XML aquí.

<?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>

La información comienza con una asignación de recursos, denominada con el nombre de familia del paquete de nuestra aplicación para UWP de destino. Entre los recursos de la asignación de recursos se incluyen dos subárboles de asignación de recursos: uno para los recursos de archivo que indexamos y otro para los recursos de cadena. Observe cómo se ha insertado el nombre de familia del paquete en todos los URI de recursos.

El primer recurso de cadena es EnOnlyString de en-US\resources.resw y tiene solo un candidato (que coincide con el calificador language-en-US). Luego está LocalizedString1 de resources.resw y en-US\resources.resw. Por lo tanto, tiene dos candidatos: uno que coincide con language-en-US y un candidato neutral de reserva que coincide con cualquier contexto. Del mismo modo, LocalizedString2 tiene dos candidatos: language-de-DE y neutral. Y, por último, NeutralOnlyString solo existe en forma neutra. Le di ese nombre para aclarar que no está destinado a localizarse.

Resumen

En este escenario, se mostró cómo usar las API de indexación de recursos de paquete (PRI) para crear un indexador de recursos. Hemos agregado recursos de cadena y archivos de recursos al indexador de recursos. A continuación, hemos usado el indexador de recursos para generar un archivo PRI binario. Y, por último, hemos volcado el archivo PRI binario en forma de XML para que podamos confirmar que contiene la información que esperábamos.

API importantes