Compartilhar via


Cenário 1: gerar um arquivo PRI a partir de recursos de cadeia de caracteres e arquivos de ativos

Nesse cenário, usaremos as APIs de indexação de recursos de pacote (PRI) para criar um aplicativo para representar nosso sistema de compilação personalizado. O objetivo desse sistema de compilação personalizado é criar arquivos PRI para um aplicativo UWP de destino. Portanto, como parte destas orientações passo a passo, criaremos alguns exemplos de arquivos de recurso (contendo cadeias de caracteres e outros tipos de recursos) para representar os recursos do aplicativo UWP de destino.

Novo projeto

Comece criando um novo projeto no Microsoft Visual Studio. Crie um projeto de Aplicativo de Console do Windows Visual C++ e nomeie como CBSConsoleApp (para o "app do console do sistema de compilação personalizado").

Escolha x64 na lista suspensaPlataformas de solução.

Cabeçalhos, biblioteca estática e dll

As APIs de PRI são declaradas no arquivo de cabeçalho MrmResourceIndexer.h (que é instalado no %ProgramFiles(x86)%\Windows Kits\10\Include\<WindowsTargetPlatformVersion>\um\). Abra o arquivo CBSConsoleApp.cpp e inclua o cabeçalho com alguns outros cabeçalhos necessários.

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

As APIs são implementadas no MrmSupport.dll, que você acessa vinculando à biblioteca estática MrmSupport.lib. Abra as Propriedades do projeto, clique em Entrada do >Vinculador, edite AdditionalDependencies e adicione MrmSupport.lib.

Crie a solução e, em seguida, copie MrmSupport.dll de C:\Program Files (x86)\Windows Kits\10\bin\<WindowsTargetPlatformVersion>\x64\ para a pasta de saída da compilação (provavelmente C:\Users\%USERNAME%\source\repos\CBSConsoleApp\x64\Debug\).

Adicione a seguinte função auxiliar ao CBSConsoleApp.cpp, pois precisaremos dela.

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

Na função main(), adicione chamadas para inicializar e desinicializar COM.

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

Arquivos de recurso pertencentes ao aplicativo UWP de destino

Agora precisaremos de alguns exemplos de arquivos de recursos (contendo cadeias de caracteres e outros tipos de recursos) para representar os recursos do aplicativo UWP de destino. Estes, é claro, podem ser encontrados em qualquer lugar do seu sistema de arquivos. Mas, nestas orientações passo a passo, será conveniente colocá-los na pasta do projeto do CBSConsoleApp para que tudo fique em um só lugar. Você só precisa adicionar esses arquivos de recursos ao sistema de arquivos; não os adicione ao projeto CBSConsoleApp.

Dentro da mesma pasta que contém CBSConsoleApp.vcxproj, adicione uma nova subpasta chamada UWPAppProjectRootFolder. Dentro dessa nova subpasta, crie esses arquivos de recurso de exemplo.

\UWPAppProjectRootFolder\sample-image.png

Esse arquivo pode conter qualquer imagem 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>

Indexar os recursos e criar um arquivo PRI

Na função main(), antes da chamada para inicializar COM, declare algumas cadeias de caracteres que precisaremos e também crie a pasta de saída na qual geraremos o arquivo 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);

Imediatamente após a chamada para inicializar COM, declare um identificador de indexador de recursos e chame MrmCreateResourceIndexer para criar um 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));

Aqui está uma explicação dos argumentos que estão sendo passados para MrmCreateResourceIndexer.

  • O nome da família do pacote do nosso aplicativo UWP de destino, que será usado como o nome do mapa de recursos quando gerarmos um arquivo PRI a partir desse indexador de recursos.
  • A raiz do projeto do nosso aplicativo UWP de destino. Em outras palavras, o caminho para nossos arquivos de recursos. Especificamos isso para que possamos especificar caminhos relativos a essa raiz em chamadas de API subsequentes para o mesmo indexador de recursos.
  • A versão do Windows com que queremos trabalhar.
  • Uma lista de qualificadores de recursos padrão.
  • Um ponteiro para nosso identificador de indexador de recursos para que a função possa defini-lo.

A próxima etapa é adicionar nossos recursos ao indexador de recursos que acabamos de criar. resources.resw é um arquivo de recursos (.resw) que contém as cadeias de caracteres neutras para nosso aplicativo UWP de destino. Role para cima (neste tópico) se quiser ver seu conteúdo. de-DE\resources.resw contém nossas cadeias de caracteres em alemão e en-US\resources.resw em inglês. Para adicionar os recursos de cadeia de caracteres em um arquivo de recursos a um indexador de recursos, chame MrmIndexResourceContainerAutoQualifiers. Em terceiro lugar, chamamos a função MrmIndexFile para um arquivo que contém um recurso de imagem neutra para o 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""));

Na chamada para MrmIndexFile, o valor L"ms-resource:///Files/sample-image.png" é o URI do recurso. O primeiro segmento de caminho é "Arquivos", e é isso que será usado como o nome da subárvore do mapa de recursos quando mais tarde gerarmos um arquivo PRI a partir desse indexador de recursos.

Tendo informado o indexador de recursos sobre nossos arquivos de recursos, é hora de fazer com que ele nos gere um arquivo PRI no disco chamando a função MrmCreateResourceFile.

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

Neste ponto, um arquivo PRI chamado resources.pri foi criado em uma pasta chamada Generated PRIs. Agora é hora de chamar MrmDestroyIndexerAndMessages para destruir seu identificador e liberar todos os recursos de máquina que ele alocou.

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

Como um arquivo PRI é binário, será mais fácil visualizar o que acabamos de gerar se despejarmos o arquivo PRI binário em seu equivalente XML. Uma chamada para MrmDumpPriFile faz exatamente isso.

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

Aqui está uma explicação dos argumentos que estão sendo passados para MrmDumpPriFile.

  • O caminho do arquivo PRI para fazer o despejo. Não estamos usando o indexador de recursos nesta chamada (acabamos de destruí-lo), então precisamos especificar um caminho de arquivo completo.
  • Nenhum arquivo de esquema. Discutiremos o que é um esquema mais adiante no tópico.
  • Apenas as informações básicas.
  • O caminho do arquivo XML a ser criado.

Isso é o que o arquivo PRI, despejado para XML aqui, contém.

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

As informações começam com um mapa de recursos, que é nomeado com o nome da família do pacote do nosso aplicativo UWP de destino. Incluídas pelo mapa de recursos estão duas subárvores de mapa de recursos: uma para os recursos de arquivo que indexamos e outra para nossos recursos de cadeia de caracteres. Observe como o nome da família do pacote foi inserido em todos os URIs de recurso.

O primeiro recurso de cadeia de caracteres é EnOnlyString de en-US\resources.resw, e ele tem apenas um candidato (que corresponde ao qualificador language-en-US. Em seguida, vem LocalizedString1 de ambos resources.resw e en-US\resources.resw. Consequentemente, ele tem dois candidatos: um language-en-US correspondente e um candidato neutro de fallback que corresponde a qualquer contexto. Da mesma forma, LocalizedString2 tem dois candidatos: language-de-DE e neutra. E, finalmente, NeutralOnlyString só existe em forma neutra. Dei esse nome para deixar claro que não é para ser localizado.

Resumo

Nesse cenário, mostramos como usar as APIs de indexação de recursos de pacote (PRI) para criar um indexador de recursos. Adicionamos recursos de cadeia de caracteres e arquivos de ativos ao indexador de recursos. Em seguida, usamos o indexador de recursos para gerar um arquivo PRI binário. E, finalmente, despejamos o arquivo PRI binário na forma de XML para que pudéssemos confirmar que ele contém as informações que esperávamos.

APIs importantes