Comments (15)
Thanks for taking a look at the API!
The string resource lookup gives you back a path to the file. You then load it however you like. That is parity behavior with the OS API. We should put a comment into the sample, and maybe have a sample for the embedded case as well.
The embedded resource is for truly embedded resources, not loose files on disk (the dev can pick whether to embed files or keep them loose). We are planning to provide wrappers that abstract this away, something like
MrmLoadStringOrEmbeddedResource(type, resource)
if (MrmType_Path == type)
{
CreateFile(resource);
ret = // load data from file
}
else
{
ret = resource;
}
return ret;
from windowsappsdk.
@axelandrejs is there any documentation on how to use this in an unpackaged application. All the options for WinUI rely on the packaging project. It would be good to see an example of using MRT Core without packaging.
We're working finalizing this support, and should have something to share very soon. The basic usage will be no different from packaged. You add Reunion to your project, and then you add assets and call APIs like you would in a WinUI app. The APIs themselves work the same.
Once we are done you won't need to do the manual steps outlined above.
from windowsappsdk.
Thanks for the comment! I have added a comment in the sample.
from windowsappsdk.
WinUI still requires MSIX packaging. I generally delete the automatically generated packaging project and use one I create myself. I was talking about merging WinUI's PRI content (which is substantial) with the manually created PRI data, in the normal context of a WinUI app running in a packaged environment. Glad I could be of service nonetheless!
from windowsappsdk.
A list of flat C APIs will provide core MRT functionality. A WinRT wrapper will build on top the core MRT to provide APIs similar to Window.ApplicationModel.Resources(.Core) and some helper APIs (e.g. locate merged resource file for resource bundle).
C APIs
DECLARE_HANDLE(MrmManagerHandle);
DECLARE_HANDLE(MrmContextHandle);
DECLARE_HANDLE(MrmMapHandle);
enum MrmType
{
MrmType_Unknown,
MrmType_String,
MrmType_Path,
MrmType_Embedded
};
struct MrmResourceData
{
UINT32 size;
void* data;
};
STDAPI MrmCreateResourceManager(_In_ PCWSTR priFileName, _Out_ MrmManagerHandle* resourceManager);
STDAPI_(void) MrmDestroyResourceManager(_In_opt_ MrmManagerHandle resourceManager);
STDAPI MrmCreateResourceContext(_In_ MrmManagerHandle resourceManager, _Out_ MrmContextHandle* resourceContext);
STDAPI_(void) MrmFreeQualifierNames(UINT32 size, _In_reads_(size) PWSTR* names);
STDAPI MrmGetAllQualifierNames(_In_ MrmContextHandle resourceContext, _Out_ UINT32* size, _Outptr_result_buffer_(*size) PWSTR** names);
STDAPI MrmGetQualifier(_In_ MrmContextHandle resourceContext, _In_ PCWSTR qualifierName, _Outptr_ PWSTR* qualifierValue);
STDAPI MrmSetQualifier(_In_ MrmContextHandle resourceContext, _In_ PCWSTR qualifierName, _In_ PCWSTR qualifierValue);
STDAPI_(void) MrmDestroyResourceContext(_In_opt_ MrmContextHandle resourceContext);
// Resource maps are owned by the resource manager and so do not need to be destroyed.
STDAPI MrmGetChildResourceMap(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmMapHandle resourceMap,
_In_ PCWSTR childResourceMapName,
_Out_ MrmMapHandle* childResourceMap);
STDAPI MrmGetResourceCount(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmMapHandle resourceMap,
_Out_ UINT32* count);
STDAPI MrmLoadStringResource(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmContextHandle resourceContext,
_In_opt_ MrmMapHandle resourceMap,
_In_ PCWSTR resourceId,
_Outptr_ PWSTR* resourceString);
STDAPI MrmLoadStringResourceFromResourceUri(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmContextHandle resourceContext,
_In_ PCWSTR resourceUri,
_Outptr_ PWSTR* resourceString);
STDAPI MrmLoadEmbeddedResource(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmContextHandle resourceContext,
_In_opt_ MrmMapHandle resourceMap,
_In_ PCWSTR resourceId,
_Out_ MrmResourceData* data);
STDAPI MrmLoadEmbeddedResourceFromResourceUri(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmContextHandle resourceContext,
_In_ PCWSTR resourceUri,
_Out_ MrmResourceData* data);
STDAPI MrmLoadStringOrEmbeddedResource(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmContextHandle resourceContext,
_In_opt_ MrmMapHandle resourceMap,
_In_ PCWSTR resourceId,
_Out_ MrmType* resourceType,
_Outptr_result_maybenull_ PWSTR* resourceString,
_Out_ MrmResourceData* data);
STDAPI MrmLoadStringOrEmbeddedFromResourceUri(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmContextHandle resourceContext,
_In_ PCWSTR resourceUri,
_Out_ MrmType* resourceType,
_Outptr_result_maybenull_ PWSTR* resourceString,
_Out_ MrmResourceData* data);
STDAPI MrmLoadStringOrEmbeddedResourceByIndex(
_In_ MrmManagerHandle resourceManager,
_In_opt_ MrmContextHandle resourceContext,
_In_opt_ MrmMapHandle resourceMap,
UINT32 index,
_Out_ MrmType* resourceType,
_Outptr_ PWSTR* resourceName,
_Outptr_result_maybenull_ PWSTR* resourceString,
_Out_ MrmResourceData* data);
STDAPI_(void*) MrmAllocateBuffer(size_t size);
STDAPI_(void) MrmFreeResource(_In_opt_ void* resource);
STDAPI MrmGetFilePathFromName(_In_ PCWSTR filename, _Outptr_ PWSTR* filePath);
WinRT APIs
namespace Microsoft.ApplicationModel.Resources
{
[contractversion(1.0)]
apicontract MrtContract{};
[contract(MrtContract, 1.0)]
[default_interface]
runtimeclass ResourceLoader
{
ResourceLoader();
ResourceLoader(String fileName);
ResourceLoader(String fileName, String resourceMap);
static String GetDefaultResourceFilePath();
String GetString(String resourceId);
String GetStringForUri(Windows.Foundation.Uri resourceUri);
}
[contract(MrtContract, 1.0)]
runtimeclass ResourceNotFoundEventArgs
{
ResourceContext Context { get; };
String Name { get; };
void SetResolvedCandidate(ResourceCandidate candidate);
}
[contract(MrtContract, 1.0)]
runtimeclass ResourceManager
{
ResourceManager();
ResourceManager(String fileName);
ResourceMap MainResourceMap { get; };
ResourceContext DefaultResourceContext { get; };
event Windows.Foundation.TypedEventHandler<ResourceManager, ResourceNotFoundEventArgs> ResourceNotFound;
}
[contract(MrtContract, 1.0)]
runtimeclass ResourceMap
{
UInt32 ResourceCount { get; };
ResourceMap GetSubtree(String reference);
ResourceCandidate GetValue(String resource);
ResourceCandidate GetValue(ResourceContext context, String resource);
Windows.Foundation.Collections.IKeyValuePair<String, ResourceCandidate> GetValueByIndex(UInt32 index);
Windows.Foundation.Collections.IKeyValuePair<String, ResourceCandidate> GetValueByIndex(ResourceContext context, UInt32 index);
}
[contract(MrtContract, 1.0)]
runtimeclass ResourceContext
{
Windows.Foundation.Collections.IMap<String, String> QualifierValues { get; };
}
[contract(MrtContract, 1.0)]
enum ResourceCandidateKind
{
Unknown = 0,
String,
FilePath,
EmbeddedData,
};
[contract(MrtContract, 1.0)]
runtimeclass ResourceCandidate
{
ResourceCandidate(ResourceCandidateKind kind, String data);
ResourceCandidate(byte[] data);
String ValueAsString { get; };
byte[] ValueAsBytes { get; };
ResourceCandidateKind Kind { get; };
}
} // namespace Microsoft.ApplicationModel.Resources
Samples
Load resources with C APIs
MrmManagerHandle resourceManager;
MrmCreateResourceManager(L"resources.pri", &resourceManager);
MrmContextHandle resourceContext;
MrmCreateResourceContext(resourceManager, &resourceContext);
MrmSetQualifier(resourceContext, L"Contrast", L"WHITE");
MrmSetQualifier(resourceContext, L"TargetSize", L"96");
// Load the asset file path
wchar_t* resourceString;
MrmLoadStringResource(
resourceManager,
resourceContext,
nullptr,
L"Files/Assets/AppList.png",
&resourceString);
MrmFreeResource(resourceString);
MrmMapHandle childResourceMap;
MrmGetChildResourceMap(
resourceManager,
nullptr,
L"Files",
&childResourceMap);
MrmMapHandle childChildResourceMap;
MrmGetChildResourceMap(
resourceManager,
childResourceMap,
L"Assets",
&childChildResourceMap);
// Load the asset file path
MrmLoadStringResource(
resourceManager,
resourceContext,
childChildResourceMap,
L"AppList.png",
&resourceString);
MrmFreeResource(resourceString);
MrmDestroyResourceContext(resourceContext);
MrmDestroyResourceManager(resourceManager);
Load resources with WinRT APIs
var resourceManager = new ResourceManager();
var candidate = resourceManager.MainResourceMap.GetValue("resources/IDS_MANIFEST_MUSIC_APP_NAME");
var value = candidate.ValueAsString;
var resourceContext = resourceManager.DefaultResourceContext;
resourceContext.QualifierValues["Language"] = "fr-FR";
candidate = resourceManager.MainResourceMap.GetValue(resourceContext, "resources/IDS_MANIFEST_MUSIC_APP_NAME");
from windowsappsdk.
One nit: I don’t think you would want to load PNG data into a variable of type wchar_t *
. I think you would want to use the MrmLoadEmbeddedResource()
function in your sample instead. Thanks!
from windowsappsdk.
When will this code be packaged and be made available on some (public) NuGet feed? Currently, I can only reference MRT Core if I use WinUI 3, since Reunion is bundled in the WinUI 3 NuGet package, or if I build it myself.
from windowsappsdk.
Guidance on how to use MRT Core in other components of Project Reunion (in code in this repository) would also be welcome.
from windowsappsdk.
Yes, our current release vehicle is WinUI. The code itself works from any context, but right now our build scripts are tied to the WinUI ones. We are working on standalone support, and will have that ready before the first full Reunion release. But I don't have a hard date to share right now.
from windowsappsdk.
@axelandrejs is there any documentation on how to use this in an unpackaged application. All the options for WinUI rely on the packaging project. It would be good to see an example of using MRT Core without packaging.
from windowsappsdk.
@nickrandolph I use MRT Core in unpackaged applications all the time; it’s part of my standard development technique now. First, you will need to vendor (include in your solution) the MRT Core projects (until and unless they are posted on NuGet independently of WinUI). I use the following technique in my private projects:
First, paste the following code into a Directory.Build.props
or other file that will be included at the top of your csproj:
<Project>
<PropertyGroup>
<MRTCoreResourceRoot>$(MSBuildProjectDirectory)\PRIResources</MRTCoreResourceRoot>
</PropertyGroup>
</Project>
Second, create a folder called PRIResources next to your csproj and dump what resources you want in there. You can include arbitrary files, and they will be embedded within the PRI file once built (assuming you use the instructions below to create your PRI file; embedding is not the default). Use *.resjson
files to localize strings, since this extension is handed specially by MRT Core. (These files are simple; there is one top-level JSON object, where its keys are identifiers used from code and the values are the localized strings.) To actually implement localization (or high-DPI support), create files named according to the following examples:
some\picture.png
some\picture.scale-200.png
some\strings.resjson
some\strings.lang-de.resjson
some\other_image.lang-de_scale-125.png
some\picture.png
and some\strings.resjson
are the neutral (fallback) versions. You put what are called qualifiers in the filename after a second dot, before the filename extension according to the above syntax (I hope I’ve made it clear what the semantics are). You can find more information about qualifiers here.
Then, create a file called priconfig.xml
somewhere you can get from MSBuild. Paste the following contents into it:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources targetOsVersion="10.0.0" majorVersion="1">
<index root="\" startIndexAt="\">
<default>
<qualifier name="Language" value="en-US"/>
<qualifier name="Contrast" value="standard"/>
<qualifier name="Scale" value="100"/>
<qualifier name="HomeRegion" value="001"/>
<qualifier name="LayoutDirection" value="LTR"/>
<qualifier name="Theme" value="light"/>
<qualifier name="AlternateForm" value=""/>
<qualifier name="DXFeatureLevel" value="DX9"/>
<qualifier name="Configuration" value=""/>
<qualifier name="DeviceFamily" value="Universal"/>
</default>
<indexer-config type="folder" foldernameAsQualifier="true" filenameAsQualifier="true" qualifierDelimiter="."/>
<indexer-config type="resw" convertDotsToSlashes="false" initialPath=""/>
<indexer-config type="resjson" initialPath=""/>
<indexer-config type="resfiles" qualifierDelimiter="."/>
<indexer-config type="EmbedFiles"/>
</index>
<index root="\" startIndexAt="\Resources">
<default>
<qualifier name="Language" value="en-US"/>
<qualifier name="Contrast" value="standard"/>
<qualifier name="Scale" value="100"/>
<qualifier name="HomeRegion" value="001"/>
<qualifier name="LayoutDirection" value="LTR"/>
<qualifier name="Theme" value="light"/>
<qualifier name="AlternateForm" value=""/>
<qualifier name="DXFeatureLevel" value="DX9"/>
<qualifier name="Configuration" value=""/>
<qualifier name="DeviceFamily" value="Universal"/>
</default>
<indexer-config type="EmbedFiles"/>
</index>
</resources>
This file (a slightly modified copy of the default used in WinUI apps) instructs makepri.exe
on how to process the contents of the $(MRTCoreResourceRoot)
. Finally, paste the following code in a Directory.Build.targets
file or some other place it will be included at the end of your csproj:
<Project>
<PropertyGroup>
<UseMRTCore Condition="'$(UseMRTCore)' == '' and '$(MRTCoreResourceRoot)' != ''">true</UseMRTCore>
<UseMRTCore Condition="'$(UseMRTCore)' == ''">false</UseMRTCore>
</PropertyGroup>
<PropertyGroup Condition="'$(UseMRTCore)' == 'true'">
<MRTCoreConfigurationFile Condition="'$(MRTCoreConfigurationFile)' == ''">path\to\priconfig.xml</MRTCoreConfigurationFile>
</PropertyGroup>
<Target Name="LocateMakePriExe" Condition="'$(UseMRTCore)' == 'true'" DependsOnTargets="PrepareForBuild">
<PropertyGroup>
<WindowsSdkDir Condition="'$(WindowsSdkDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@InstallationFolder)</WindowsSdkDir>
<WindowsSdkDir Condition="'$(WindowsSdkDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@InstallationFolder)</WindowsSdkDir>
</PropertyGroup>
<PropertyGroup>
<MakePRIVersion Condition="'$(MakePRIVersion)' == ''">10.0.18362.0</MakePRIVersion>
<MakePRIPath>$(WindowsSdkDir)bin\$(MakePRIVersion)\x64\makepri.exe</MakePRIPath>
</PropertyGroup>
</Target>
<Target Name="CreatePRIFile" Condition="'$(UseMRTCore)' == 'true'" DependsOnTargets="LocateMakePriExe"
Inputs="$(MRTCoreResourceRoot)\**\*.*" Outputs="$(IntermediateOutputPath)$(TargetName).manual.pri">
<Error Condition="'$(MRTCoreResourceRoot)' == ''" Text="MRTCoreResourceRoot must be set" />
<Error Condition="HasTrailingSlash('$(MRTCoreResourceRoot)')" Text="MRTCoreResourceRoot property cannot end in a backslash" />
<PropertyGroup>
<MRTCoreResourceRootAbsolute>$([System.IO.Path]::GetFullPath('$(MRTCoreResourceRoot)'))</MRTCoreResourceRootAbsolute>
</PropertyGroup>
<Exec Command=""$(MakePRIPath)" new /pr "$(MRTCoreResourceRootAbsolute)" /cf "$(MRTCoreConfigurationFile)" /of "$(IntermediateOutputPath)$(TargetName).manual.pri" /o" />
<Exec Command=""$(MakePRIPath)" resourcepack /pr "$(MRTCoreResourceRootAbsolute)" /cf "$(MRTCoreConfigurationFile)" /if "$(IntermediateOutputPath)$(TargetName).manual.pri" /of "$(IntermediateOutputPath)$(TargetName).manual.pri" /o" />
<ItemGroup>
<_PriFile Include="$(IntermediateOutputPath)$(TargetName).manual.pri" />
<FileWrites Include="$(IntermediateOutputPath)$(TargetName).manual.pri" />
</ItemGroup>
<Copy Condition="'$(PriProjTaskAssembly)' == ''" SkipUnchangedFiles="true" UseHardlinksIfPossible="true"
SourceFiles="$(IntermediateOutputPath)$(TargetName).manual.pri" DestinationFiles="$(IntermediateOutputPath)$(TargetName).pri" />
<ItemGroup Condition="'$(PriProjTaskAssembly)' == ''">
<Content Include="$(IntermediateOutputPath)$(TargetName).pri">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
</Content>
<FileWrites Include="$(IntermediateOutputPath)$(TargetName).pri" />
</ItemGroup>
</Target>
<PropertyGroup>
<PrepareResourcesDependsOn>
CreatePRIFile;
$(PrepareResourcesDependsOn)
</PrepareResourcesDependsOn>
</PropertyGroup>
You’ll need to update the value of the MRTCoreConfigurationFile
property to point to where your priconfig.xml
file is. When this MSBuild code is run, it will generate a PRI file named after your assembly, located next to the DLL on disk. You can then use the MRT Core APIs to extract data from it. It is completely self-contained; the source files do not need to be distributed, only the generated PRI file.
Unfortunately, the C# code required for retrieving data from the PRI file (the code that calls the MRT Core APIs) uses a path syntax that is difficult to explain here, because it depends on the structure and contents of that PRIResources folder you created earlier. If you want to see the contents of your PRI file, run makepri.exe dump /if AssemblyName.pri /of pri.xml /o
and examine the resulting XML file. I recommend always doing this at the start, as the contents of that XML file will give you the name/URI syntax that is required to lookup resources.
As a finishing touch, the above code will not conflict with WinUI’s usage of MRT Core; if you ever add WinUI to your project, the contents of your manually generated PRI file will be merged into WinUI’s automatically generated PRI file during the build. The two chunks of data will coexist perfectly; the names/URIs you use to retrieve the resources won’t even change. Let me know if you have any more questions. Hope this helps!
from windowsappsdk.
@wjk that's awesome - thanks so much for this information. You mentioned adding WinUI to the project in the final paragraph - is this something you've done successfully and been able to run the app without packaging? If so, would you be willing to share a sample as I think this is an important scenario that I would love to see working.
from windowsappsdk.
@jonwis I noted that this is now "code complete" yet according to the roadmap support for unpackaged apps won't appear until v0.8 (preview). Should we be expecting these issues to be in sync with the roadmap?
from windowsappsdk.
0.8 Preview is now available, and there is an unpackaged sample app available - https://github.com/microsoft/WindowsAppSDK-Samples/tree/main/MrtCore/console_unpackaged_app However, it only demonstrates how to work with strings (resw), and not images or other resources. It'd be good to have some additional on how to work with MRT Core in this scenario.
from windowsappsdk.
Hi everyone! Since this was shipped in 1.0 we're going to go ahead and close the issue. However, if there is additional functionality you would like to see in the product please submit further feedback to our feature portal!
from windowsappsdk.
Related Issues (20)
- Resw aka Resources can't be Accessed Between WinUI Based Class Library Projects in Xaml HOT 4
- ResourceLoader can't find resources HOT 5
- Provide a machine readable releases.json with EOL dates
- Main build failure due to missing nuget packages HOT 1
- Inconsistent behaviour of windows App SDK runtime Installer version 1.3
- CheckUpdateAvailabilityAsync crashes when not in debug mode
- [Docs] [Depedency] WinAppSDK app compiles fine but is unable to be deployed - Provide the framework "Microsoft.VCLibs.140.00.Debug.UWPDesktop"
- WindowsAppSDK-VersionInfo.json: Release.MajorMinor.HexUInt32 is not hex value HOT 3
- CredUIPromptForWindowsCredentials crashes in unpackaged app HOT 28
- Mapping between a XAML namespace and a CLR namespace in WinUI 3 HOT 2
- ProximityDevice NFC events not triggered / NFC doesn't work at all HOT 8
- Question: Can I redistribute `Microsoft.Windows.SDK.NET.dll` and `WinRT.Runtime.dll`? HOT 5
- Random coreclr.dll Access Violations HOT 4
- .NET framework 4.8 SDK, .NET Native is auto checked
- windowsappsdk can't build with normal checkout, nuget restore keep asking private/internal microsoft azure aritfact credential HOT 3
- Exception Thrown by comparison of TargetPlatformVersion HOT 3
- Unable to rotate 180 degrees and crop with the Jpeg bitmap encoder
- dependabot fails to update Microsoft.WindowsAppSDK version HOT 2
- Creating a singleExe from UnPackaged app breaks the latest WindowsApp SDK HOT 5
- UWP => WinUI3 migration: PasswordVault cannot be accessed anymore HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from windowsappsdk.