Giter Club home page Giter Club logo

Comments (15)

axelandrejs avatar axelandrejs commented on May 21, 2024 3

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 avatar axelandrejs commented on May 21, 2024 3

@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.

huichen123 avatar huichen123 commented on May 21, 2024 1

Thanks for the comment! I have added a comment in the sample.

from windowsappsdk.

wjk avatar wjk commented on May 21, 2024 1

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.

huichen123 avatar huichen123 commented on May 21, 2024

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.

wjk avatar wjk commented on May 21, 2024

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.

wjk avatar wjk commented on May 21, 2024

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.

wjk avatar wjk commented on May 21, 2024

Guidance on how to use MRT Core in other components of Project Reunion (in code in this repository) would also be welcome.

from windowsappsdk.

axelandrejs avatar axelandrejs commented on May 21, 2024

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.

nickrandolph avatar nickrandolph commented on May 21, 2024

@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.

wjk avatar wjk commented on May 21, 2024

@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="&quot;$(MakePRIPath)&quot; new /pr &quot;$(MRTCoreResourceRootAbsolute)&quot; /cf &quot;$(MRTCoreConfigurationFile)&quot; /of &quot;$(IntermediateOutputPath)$(TargetName).manual.pri&quot; /o" />
    <Exec Command="&quot;$(MakePRIPath)&quot; resourcepack /pr &quot;$(MRTCoreResourceRootAbsolute)&quot; /cf &quot;$(MRTCoreConfigurationFile)&quot; /if &quot;$(IntermediateOutputPath)$(TargetName).manual.pri&quot; /of &quot;$(IntermediateOutputPath)$(TargetName).manual.pri&quot; /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.

nickrandolph avatar nickrandolph commented on May 21, 2024

@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.

nickrandolph avatar nickrandolph commented on May 21, 2024

@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.

MarkIngramUK avatar MarkIngramUK commented on May 21, 2024

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.

btueffers avatar btueffers commented on May 21, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.