2012-06-09 16 views
18

Tengo una biblioteca portátil que estoy usando para una aplicación de Windows Phone. En esa misma Biblioteca portátil, tengo un par de archivos de contenido (Acción de compilación = Contenido).¿Cómo leer un archivo de recursos dentro de una Biblioteca de clases portátil?

Creé una clase DataReader en la Biblioteca portátil que se supone que me devuelve una secuencia al archivo de contenido. Sin embargo, con el siguiente código estoy recibiendo constantemente null de GetManifestResourceStream. ¿Qué estoy haciendo mal?

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

Respuesta

0

si ha añadido archivos como recursos, revisar sus .Designer.cs, habrá una propiedad para cada recurso. puedes acceder desde eso.

aquí es el auto muestra de generar la propiedad de los recursos de archivos DAT

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

se puede leer el archivo DAT como

System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding(); 
    var str = enc.GetString(Resource1.MyDatFile); 
+1

estoy tratando de leer un archivo con la Acción de generación ajustado a contenido. – Martin

-1

Es necesario utilizar el método Application.GetResourceStream en lugar de utilizar GetManifestResource corriente

Referencia: 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)); 
+2

No se puede acceder a la clase estática de la aplicación desde la Biblioteca de clases. – Martin

2

Agregue su archivo a un recurso portátil y establezca la acción de compilación en Recurso incrustado. Por ejemplo, archivos GB.png, US.png en la carpeta CountryFlags.

Agregue una función getter con un código como este (aquí es específico para nuestra imagen countrybus getter).

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

     return rs; 
    } 
} 

Aquí Full.DLL.Name es la parte de la biblioteca portátil generado que está delante de .dll extensión. (Nota:Anything.Resources.dll es un nombre incorrecto para una biblioteca porque Visual Studio lo ignora al menos al generar XAP, etc. en su lugar, por ejemplo, Anything.PortableResource.dll funcionará).

+0

'Assembly.GetExecutingAssembly()' no está disponible en una PCL (al menos cuando se dirige a las plataformas modernas). –

+0

@AndrewArnott ¿estás seguro? PCL bajo 4.5 está marcado como compatible: http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getexecutingassembly%28v=vs.110%29.aspx Este código también ha funcionado .NET 4, 4.5 y Silverlight (a través de PCL). –

+0

Supongo que no está disponible cuando el objetivo es Win8 + Net45 + WP80 –

2

Simplemente respondiendo a la solicitud de recompensa. Primero, usar Build Action = Content no afecta en absoluto la construcción. Es una propiedad del elemento del proyecto que otras herramientas pueden leer. Un constructor de instalador lo usa, por ejemplo, para descubrir que el archivo debe incluirse en el programa de instalación y desplegarse en la máquina del usuario.

Usar Build Action = Recurso incrustado como se indica en la pregunta upvoted fue la supervisión del OP. De hecho, esto instruye a MSBuild para incrustar el archivo como un recurso en el manifiesto del ensamblado, utilizando Assembly.GetManifestResourceStream() lo recupera en el tiempo de ejecución.

Pero está bastante claro por el comentario de recompensa que tampoco quieres eso. La alternativa es simplemente copiar el archivo en la máquina de destino. Donde se sentará pacientemente hasta que lo necesite. Notable sobre esto es que no de ninguna manera altera el tamaño del paquete que el usuario descarga de la Tienda. Se necesita la misma cantidad de espacio, ya sea dentro del ensamblaje o en un archivo separado en el paquete.

Arrastra eso como una forma de salir adelante.

Hace una diferencia en el tiempo de ejecución, todo el conjunto se asigna a la memoria virtual cuando se carga. Por lo tanto, un ensamblaje con un recurso tendrá más espacio de memoria virtual. Pero la palabra "virtual" es muy importante, se requieren muy pocos recursos del teléfono. Solo unos pocos bytes en las tablas de asignación de páginas para cada 4096 bytes en el recurso. No comienza a pagar por la memoria virtual hasta que se acceda. En ese punto, el sistema operativo del teléfono necesita realmente convertirlo de la memoria virtual a la física. O en otras palabras, cargue los bytes del recurso en la RAM. Esto no es diferente de cargar un archivo, también se carga en la RAM cuando lo abres.

Arrastra eso como una forma de salir adelante.

Nos estamos quedando sin buenas razones para hacer esto, Microsoft ciertamente escogió la forma predeterminada de manejar los recursos como una mejor práctica. Es. Pero a veces usted tiene para implementar contenido como un archivo, simplemente porque es demasiado grande. Uno que está empujando 2 gigabytes o más, consumiendo toda la memoria virtual en un sistema operativo de 32 bits, por lo que posiblemente no se pueda asignar a la máquina virtual. El programa simplemente no podrá comenzar. Este no es el tipo de programa que un usuario de teléfono va a estar muy satisfecho, realmente.

Luego debe enfocarse en la fase de compilación de la solución, el último paso cuando se construye una aplicación de teléfono. En el que se han compilado todos los proyectos de la solución y se ha creado el archivo único que se carga en la Tienda y que el usuario descarga.

Y sí, hay un problema allí, MSBuild no es lo suficientemente inteligente como para ver la biblioteca PCL que utiliza el recurso. La acción Build = Content debería ser lo suficientemente buena, como lo es para un instalador, pero eso no funciona. Se solo empaqueta el archivo DLL, no el recurso. Fue hecho para asumir que lo había incrustado, la solución de mejores prácticas.

Lo que tienes que hacer es anular el manifiesto del paquete. Descrito en this MSDN article. Muy, muy feo, estás mirando un cursor parpadeante en blanco. Que es donde me estoy quedando sin buenos consejos, esto fue hecho para no hacer.

+1

Supongo que no estoy seguro de qué hacer con esta respuesta. Podría ser una disertación brillante o nada en absoluto que necesite. Quizás podría simplificar esto: tengo una aplicación WP8, una WinRT y una PCL. La aplicación tiene toneladas de archivos de video, audio, imagen y xml que ambas aplicaciones RT/WP8 necesitan usar periódicamente. Alrededor de 700 MB. ¿Por qué no ponerlos en PCL? Me pregunto? ¿Pero la DLL para la PCL realmente ahora será de 700 MB si los incrusto?Necesito esa DLL todo el tiempo para otras cosas. ¿Qué hacer? Agregar como Build Action = Content los mantiene fuera del PCL hasta que accedan. Vaya, no hay más espacio para escribir ... –

+0

El problema es que desea que su PCL haga algo que no se supone que debe realizar una PCL, el acceso a archivos no es portátil en todas las plataformas, por lo que si marca sus archivos como Contenido, necesita acceder a ellos como cualquier otro archivo, una solución alternativa es incrustarlos, pero al hacerlo se infla su DLL y puede colapsar cualquier aplicación que use su dll, eso es lo que Hans intenta decirle. – Rafael

8

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

acceso del archivo no se puede hacer portable entre aplicaciones Windows Store y de Windows Phone 8 aplicaciones. Deberá usar el código específico de la plataforma para abrir el archivo y adquirir una transmisión. A continuación, puede pasar la secuencia en la PCL.

Si la compila con la acción de compilación de contenido, el XML no está dentro de la DLL. Está en el sistema de archivos, y no hay forma de obtenerlo desde dentro de la PCL. Es por eso que todas las respuestas establecen la acción de compilación en Recurso incrustado. Coloca el archivo dentro de MyPCL.DLL\Path\To\Content.xml.

Sin embargo, si se establece la acción de generación para contenido y establecer el tipo de copia a Copiar si es más nuevo, colocará sus archivos en el mismo directorio que el ejecutable.

Solution Explorer, Properties, and Windows Explorer

Por lo tanto, podemos simplemente colocar una interfaz para leer el archivo en nuestra PCL. Al iniciar nuestro código no portátil, inyectamos una implementación en PCL.

namespace TestPCLContent 
{ 
    public interface IContentProvider 
    { 
     string LoadContent(string relativePath); 
    } 
} 

namespace TestPCLContent 
{ 
    public class TestPCLContent 
    { 
     private IContentProvider _ContentProvider; 
     public IContentProvider ContentProvider 
     { 
      get 
      { 
       return _ContentProvider; 
      } 
      set 
      { 
       _ContentProvider = value; 
      } 
     } 

     public string GetContent() 
     { 
      return _ContentProvider.LoadContent(@"Content\buildcontent.xml"); 
     } 
    } 
} 

Ahora que el PCL se ha definido anteriormente, podemos crear nuestra implementación de la interfaz en el código no portátil (abajo):

namespace WPFBuildContentTest 
{ 
    class ContentProviderImplementation : IContentProvider 
    { 
     private static Assembly _CurrentAssembly; 

     private Assembly CurrentAssembly 
     { 
      get 
      { 
       if (_CurrentAssembly == null) 
       { 
        _CurrentAssembly = System.Reflection.Assembly.GetExecutingAssembly(); 
       } 

       return _CurrentAssembly; 
      } 
     } 

     public string LoadContent(string relativePath) 
     { 
      string localXMLUrl = Path.Combine(Path.GetDirectoryName(CurrentAssembly.GetName().CodeBase), relativePath); 
      return File.ReadAllText(new Uri(localXMLUrl).LocalPath); 
     } 
    } 
} 

El inicio de la aplicación, se inyecta la puesta en práctica y demostrar contenidos de carga.

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 

      string content = tpc.GetContent(); //loading 
     } 
    } 
} 

EDIT: que lo mantuvieron cadenas en lugar de corrientes por razones de simplicidad.

+0

Esto es interesante. Estoy familiarizado con este tipo de inyección. Pero este es el problema: no necesito la aplicación WinRT o WP8 para llamar a la PCL y obtener un recurso. Necesito la PCL misma para acceder al recurso "Build Action = Content" que está dentro de sí mismo. Una vez que lo tiene, hará algo con él (como llenar una propiedad en un modelo de vista, por ejemplo). –

+1

@ToddMain, me dejé llevar por el ejemplo, pero fue solo un ejemplo. La PCL es una DLL. Eso significa que lo estás llamando desde el código no portátil de todos modos. Una vez que hayas inyectado la implementación de la interfaz, la PCL puede cargar internamente los archivos y hacer lo que quiera con ellos. –

+0

@ToddMain, ¿lo descubrieron? –

31

Su camino está equivocado. Está utilizando barras diagonales, pero en el nombre del recurso manifiesto incrustado, las barras se convirtieron en puntos durante la construcción. Además, dependiendo de las plataformas específicas de su PCL, es posible que ni siquiera pueda llamar al Assembly.GetExecutingAssembly().

Esto es lo que puede hacer:

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

// Use this help aid to figure out what the actual manifest resource name is. 
string[] resources = assembly.GetManifestResourceNames(); 

// Once you figure out the name, pass it in as the argument here. 
Stream stream = assembly.GetManifestResourceStream("Some.Path.AndFileName.Ext"); 
+0

+1 for 'GetTypeInfo' –

+0

Esto definitivamente funciona, aún así desearía que la forma contenta de hacer las cosas funcionara aunque –

+0

¿Qué es AnyTypeInYourAssembly? Nadie explica cuál es esta opción. Ni siquiera en los documentos de Xamarin. – Dpedrinha

0
var assembly = typeof(PegHelper).GetTypeInfo().Assembly; 
using (var stream = assembly.GetManifestResourceStream("Parsers.Peg.SelfDef.xml")) 
using (var reader = new StreamReader(stream)) 
{ 
    string xmlText = reader.ReadToEnd(); 
    return XDocument.Parse(xmlText); 
} 
+0

Explica tu código. Solo poner código no es suficiente. –

+0

¿Qué es PegHelper? Nadie explica cuál es esta opción. Ni siquiera en los documentos de Xamarin. – Dpedrinha

0

En primer lugar, recuperar su montaje como éste (DataLoader siendo una clase en su conjunto del PCL):

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

Añadir su file to portable resource y establecer acción de compilación en el recurso incrustado.

A continuación, puede recuperar sus ressource así:

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

Por ejemplo si tengo una logo.png archivo en una carpeta "Activos/Logos" en una asamblea "TvShowTracker.Helpers" Voy a utilizar este código:

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

feliz de codificación :)

Cuestiones relacionadas