Comment lire un fichier de ressources dans une bibliothèque de classes portable?

J’ai une bibliothèque portable que j’utilise pour une application Windows Phone. Dans cette même bibliothèque portable, j’ai quelques fichiers de contenu ( Build Action = Content ).

J’ai créé une classe DataReader dans la bibliothèque portable qui est censée me renvoyer un stream dans le fichier de contenu. Cependant, avec le code ci-dessous, je récupère systématiquement la valeur null de GetManifestResourceStream . Qu’est-ce que je fais mal?

 public class DataReader { public static Stream GetStream(ssortingng code) { ssortingng path = ssortingng.Format("./data/code-{0}.dat", code); return Assembly.GetExecutingAssembly().GetManifestResourceStream(path); } } 

Votre chemin est faux. Vous utilisez des barres obliques, mais dans les noms de ressources de manifeste incorporés, les barres obliques ont été converties en points au cours de la génération. De plus, en fonction de vos plates-formes ciblées PCL, vous ne pourrez peut-être même pas appeler Assembly.GetExecutingAssembly() .

Voici ce que vous pouvez faire:

 var assembly = typeof(AnyTypeInYourAssembly).GetTypeInfo().Assembly; // Use this help aid to figure out what the actual manifest resource name is. ssortingng[] resources = assembly.GetManifestResourceNames(); // Once you figure out the name, pass it in as the argument here. Stream stream = assembly.GetManifestResourceStream("Some.Path.AndFileName.Ext"); 

De http://social.msdn.microsoft.com/Forums/windowsapps/en-US/386eb3b2-e98e-4bbc-985f-fc143db6ee36/read-local-file-in-portable-library#386eb3b2-e98e-4bc-985f -fc143db6ee36

L’access aux fichiers ne peut pas être effectué de manière portable entre les applications Windows Store et Windows Phone 8. Vous devrez utiliser un code spécifique à la plate-forme pour ouvrir le fichier et acquérir un stream. Vous pouvez ensuite passer le stream dans la PCL.

Si vous le créez avec l’action de génération de contenu, le code XML n’est pas à l’intérieur de la DLL. C’est sur le système de fichiers, et il n’y a aucun moyen de l’obtenir depuis l’intérieur du PCL. C’est pourquoi toutes les réponses définissent l’action de génération sur Ressources incorporées . Il place le fichier dans MyPCL.DLL\Path\To\Content.xml .

Toutefois , si vous définissez l’action de génération sur Contenu et définissez le type de copie sur Copier s’il est plus récent , vos fichiers seront placés dans le même répertoire que l’exécutable.

Explorateur de solutions, Propriétés et Explorateur Windows

Par conséquent, nous pouvons simplement placer une interface pour lire le fichier dans notre PCL. Au démarrage de notre code non-portable, nous injectons une implémentation dans le PCL.

 namespace TestPCLContent { public interface IContentProvider { ssortingng LoadContent(ssortingng relativePath); } } namespace TestPCLContent { public class TestPCLContent { private IContentProvider _ContentProvider; public IContentProvider ContentProvider { get { return _ContentProvider; } set { _ContentProvider = value; } } public ssortingng GetContent() { return _ContentProvider.LoadContent(@"Content\buildcontent.xml"); } } } 

Maintenant que le PCL est défini ci-dessus, nous pouvons créer notre implémentation d’interface en code non-portable (ci-dessous):

 namespace WPFBuildContentTest { class ContentProviderImplementation : IContentProvider { private static Assembly _CurrentAssembly; private Assembly CurrentAssembly { get { if (_CurrentAssembly == null) { _CurrentAssembly = System.Reflection.Assembly.GetExecutingAssembly(); } return _CurrentAssembly; } } public ssortingng LoadContent(ssortingng relativePath) { ssortingng localXMLUrl = Path.Combine(Path.GetDirectoryName(CurrentAssembly.GetName().CodeBase), relativePath); return File.ReadAllText(new Uri(localXMLUrl).LocalPath); } } } 

Au démarrage de l’application, nous injectons l’implémentation et démontrons le contenu du chargement.

 namespace WPFBuildContentTest { //App entrance point. In this case, a WPF Window public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { ContentProviderImplementation cpi = new ContentProviderImplementation(); TestPCLContent.TestPCLContent tpc = new TestPCLContent.TestPCLContent(); tpc.ContentProvider = cpi; //injection ssortingng content = tpc.GetContent(); //loading } } } 

EDIT: J’ai gardé les chaînes au lieu de Streams pour plus de simplicité.

Je réponds juste à la demande de prime. Tout d’abord, l’utilisation de Build Action = Content n’affecte pas réellement la construction. C’est une propriété d’élément de projet que d’autres outils peuvent lire. Un constructeur d’installation l’utilise par exemple pour déterminer que le fichier doit être inclus dans le programme d’installation et déployé sur la machine de l’utilisateur.

L’utilisation de l’action Construire = la ressource incorporée, comme indiqué dans la question à vote positif, était la supervision du PO. En réalité, cela indique à MSBuild d’incorporer le fichier en tant que ressource dans le manifeste d’assembly, à l’aide de Assembly.GetManifestResourceStream (), qui le récupère au moment de l’exécution.

Mais il ressort clairement du commentaire sur la prime que vous ne le souhaitez pas non plus. Le repli consiste simplement à copier le fichier sur la machine cible. Où il s’assiéra patiemment jusqu’à ce que vous en ayez besoin. Il est à noter que cela ne modifie en aucune façon la taille du paquet que l’utilisateur télécharge depuis le magasin. Cela prend la même quantité d’espace, que ce soit à l’intérieur de l’assemblage ou dans un fichier séparé du paquet.

Donc, grattez cela pour aller de l’avant.

Cela fait une différence au moment de l’exécution: l’ensemble de l’assemblage est mappé dans la mémoire virtuelle lorsqu’il est chargé. Ainsi, un assemblage avec une ressource utilisera davantage d’espace mémoire virtuel. Mais le mot “virtuel” est très important, cela prend très peu de ressources du téléphone. Quelques octets dans les tables de mappage de page pour chaque 4096 octets de la ressource. Vous ne commencez pas à payer pour la mémoire virtuelle tant qu’elle n’est pas accessible. À quel moment le système d’exploitation du téléphone doit-il réellement passer de la mémoire virtuelle à la mémoire physique. Ou en d’autres termes, chargez les octets de la ressource dans la RAM. Ce n’est pas différent du chargement d’un fichier, il est également chargé dans la RAM lorsque vous l’ouvrez.

Donc, grattez cela pour aller de l’avant.

Nous manquons de bonnes raisons pour faire cela. Microsoft a certainement choisi le moyen par défaut de gérer les ressources en tant que meilleure pratique. Il est. Mais parfois, vous devez déployer le contenu sous forme de fichier, simplement parce qu’il est trop volumineux. Celui qui pousse 2 gigaoctets ou plus, utilise toute la mémoire virtuelle sur un système d’exploitation 32 bits et ne peut donc pas être mappé sur un ordinateur virtuel. Le programme ne pourra tout simplement pas démarrer. Ce n’est pas le genre de programme dont un utilisateur de téléphone sera vraiment ravi.

Vous devez ensuite vous concentrer sur la phase de compilation de la solution, la dernière étape lors de la création d’une application téléphonique. Celui où tous les projets de la solution ont été compilés et où le fichier unique chargé dans la boutique et téléchargé par l’utilisateur est créé.

Et oui, il y a un problème, MSBuild n’est pas assez intelligent pour voir la bibliothèque PCL utiliser la ressource. L’action Construire = Le contenu devrait être suffisant, comme pour un installateur, mais cela ne fonctionne pas. Il ne conditionnera que la DLL, pas la ressource. Il a été fait pour supposer que vous l’intègreriez, la meilleure solution.

Ce que vous devez faire est de remplacer le manifeste du paquet. Décrit dans cet article MSDN . Très, très moche, vous regardez un curseur vide clignotant. C’est là que je manque de bons conseils, cela a été fait pour ne pas le faire.

Ajoutez votre fichier à une ressource portable et définissez l’action de génération sur Embedded Resource . Par exemple, les fichiers GB.png , US.png sous le dossier CountryFlags .

Ajoutez une fonction getter avec un code comme celui-ci (ici, elle est spécifique à notre image getter countryflag).

 public class CountryFlags { public static Stream GetFlagStream(ssortingng countryIsoCode2ch) { var flagname = "Full.DLL.Name.CountryFlags.{0}.png"; var rs = Assembly.GetExecutingAssembly().GetManifestResourceStream( ssortingng.Format(flagname, countryIsoCode2ch)); return rs; } } 

Full.DLL.Name est ici la partie de la bibliothèque portable générée qui est avant l’extension .dll . ( Remarque: Anything.Resources.dll est un nom incorrect pour une bibliothèque car il est ignoré par Visual Studio, du moins lors de la génération de XAP, etc .; à la place par exemple, Anything.PortableResource.dll fonctionnera).

Si vous avez ajouté des fichiers en tant que ressources, vérifiez votre .Designer.cs, il y aura une propriété pour chaque ressource. vous pouvez accéder à partir de cela.

Voici l’exemple de propriété Auto Gener pour les ressources de fichier dat.

  internal static byte[] MyDatFile { get { object obj = ResourceManager.GetObject("MyDatFile", resourceCulture); return ((byte[])(obj)); } 

vous pouvez lire le fichier dat comme

  System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding(); var str = enc.GetSsortingng(Resource1.MyDatFile); 
 var assembly = typeof(PegHelper).GetTypeInfo().Assembly; using (var stream = assembly.GetManifestResourceStream("Parsers.Peg.SelfDef.xml")) using (var reader = new StreamReader(stream)) { ssortingng xmlText = reader.ReadToEnd(); return XDocument.Parse(xmlText); } 

Tout d’abord, récupérez votre assemblage comme ceci (DataLoader étant une classe de votre assemblage PCL):

 var assembly = typeof(DataLoader).GetTypeInfo().Assembly; 

Ajoutez votre fichier à une ressource portable et définissez l’action de génération sur Embedded Resource .

Ensuite, vous pouvez récupérer votre ressource comme ceci:

 ssortingng resourceNam= "to be filled"; var assembly = typeof(DataLoader).GetTypeInfo().Assembly; var compressedStream = assembly.GetManifestResourceStream(resourceName)); 

Par exemple, si j’ai un fichier logo.png dans un dossier “Assets / Logos” dans un assemblage “TvShowTracker.Helpers”, j’utiliserai ce code:

 ssortingng resourceNam= "TvShowTracker.Helpers.Assets.Logos.logo.png"; var assembly = typeof(DataLoader).GetTypeInfo().Assembly; var compressedStream = assembly.GetManifestResourceStream(resourceName)); 

Bonne codage 🙂

Vous devez utiliser la méthode Application.GetResourceStream au lieu d’utiliser le stream GetManifestResource

Référence: http://msdn.microsoft.com/en-us/library/ms596994%28v=vs.95%29.aspx

 var albumArtPlaceholder = Application.GetResourceStream( new Uri("Images/artwork.placeholder.png", UriKind.Relative));