c# - read - ¿Cómo leer un archivo de recursos dentro de una biblioteca de clases portátil?
resources c# resx (8)
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 ( Build Action = Content ).
DataReader
una clase DataReader
en la biblioteca portátil que se supone que me devuelva una secuencia al archivo de contenido. Sin embargo, con el código que GetManifestResourceStream
continuación, estoy volviendo constantemente null
desde 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);
}
}
Agregue su archivo a un recurso portátil y establezca la acción de compilación en Recurso incrustado . Por ejemplo, los archivos GB.png
, US.png
en la carpeta CountryFlags
.
Agregue una función de obtención con un código como este (aquí es específico para nuestra imagen de obtención de bandera del país).
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 generada que está antes de la extensión .dll
. ( Nota: Anything.Resources.dll
es un mal nombre para una biblioteca porque Visual Studio la ignora al menos al generar XAP, etc.; en cambio, por ejemplo, Anything.PortableResource.dll
funcionará).
El acceso a los archivos no se puede hacer de manera portátil entre las aplicaciones de la Tienda Windows y las aplicaciones de Windows Phone 8. Tendrá que usar un código específico de la plataforma para abrir el archivo y adquirir un flujo. A continuación, puede pasar la secuencia en el PCL.
Si lo creas 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 manera 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 configura la acción de compilación en Contenido y configura el tipo de copia en Copiar si es más reciente , colocará sus archivos en el mismo directorio que el ejecutable.
Por lo tanto, solo podemos colocar una interfaz para leer el archivo en nuestro PCL. Al iniciar nuestro código no portátil, inyectamos una implementación en el 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 la PCL está definida anteriormente, podemos crear nuestra implementación de interfaz en código no portátil (a continuación):
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);
}
}
}
En el inicio de la aplicación, inyectamos la implementación y demostramos la carga de contenidos.
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
}
}
}
EDITAR: He mantenido cadenas en lugar de Streams por simplicidad.
Debe utilizar el método Application.GetResourceStream en lugar de usar la secuencia GetManifestResource
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));
En primer lugar, recupere su ensamblaje de esta manera (DataLoader es una clase en su ensamblaje PCL):
var assembly = typeof(DataLoader).GetTypeInfo().Assembly;
Agregue su archivo a un recurso portátil y configure la acción de compilación en Recurso incrustado .
Entonces puedes recuperar tu recurso de esta manera:
string resourceNam= "to be filled";
var assembly = typeof(DataLoader).GetTypeInfo().Assembly;
var compressedStream = assembly.GetManifestResourceStream(resourceName));
Por ejemplo, si tengo un archivo logo.png en una carpeta "Activos / Logotipos" en un ensamblaje "TvShowTracker.Helpers" usaré este código:
string resourceNam= "TvShowTracker.Helpers.Assets.Logos.logo.png";
var assembly = typeof(DataLoader).GetTypeInfo().Assembly;
var compressedStream = assembly.GetManifestResourceStream(resourceName));
Feliz codificacion :)
Sólo respondiendo a la solicitud de recompensa. En primer lugar, el uso de Build Action = Content no afecta en absoluto a la compilación. Es una propiedad del elemento del proyecto que otras herramientas pueden leer. Un creador de instaladores lo usa, por ejemplo, para darse cuenta de que el archivo debe incluirse en el programa de instalación y desplegarse en la máquina del usuario.
El uso de Build Action = Embedded Resource como se indica en la pregunta upvoted fue la supervisión del OP. Eso en realidad le indica a MSBuild que incruste el archivo como un recurso en el manifiesto de ensamblaje, utilizando Assembly.GetManifestResourceStream () lo recupera en tiempo de ejecución.
Pero está bastante claro a partir del comentario generoso que usted tampoco quiere eso. El respaldo es simplemente copiar el archivo en la máquina de destino. Donde se sentará pacientemente hasta que lo necesites. Notable de esto es que esto no altera de ninguna manera el tamaño del paquete que el usuario descarga de la Tienda. Toma la misma cantidad de espacio, ya sea dentro del ensamblaje o un archivo separado en el paquete.
Así que rasca eso como una forma de salir adelante.
Hace una diferencia en el tiempo de ejecución, todo el ensamblaje se asigna a la memoria virtual cuando se carga. Así que un conjunto con un recurso tomará más espacio de memoria virtual. Pero la palabra "virtual" es muy importante, requiere muy pocos recursos del teléfono. Solo unos pocos bytes en las tablas de asignación de páginas por cada 4096 bytes en el recurso. No comienzas a pagar por la memoria virtual hasta que se accede. En ese momento, el sistema operativo del teléfono necesita convertirlo de virtual a físico. 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 se abre.
Así que rasca eso como una forma de salir adelante.
Nos estamos quedando sin buenas razones para hacer esto, Microsoft ciertamente eligió la forma predeterminada de manejar los recursos como la mejor práctica. Es. Pero a veces hay que implementar el contenido como un archivo, simplemente porque es demasiado grande. Uno que impulsa 2 gigabytes o más, que consume toda la memoria virtual en un sistema operativo de 32 bits, por lo que posiblemente no se puede asignar a la máquina virtual. El programa simplemente no podrá comenzar. Este no es el tipo de programa con el que un usuario del teléfono estará realmente satisfecho.
A continuación, debe centrarse en la fase de compilación de la solución, el último paso cuando se construye una aplicación de teléfono. Aquel en el que se han compilado todos los proyectos de la solución y se crea el archivo de una sola vez que se carga en la Tienda y es descargado por el usuario.
Y sí, hay un problema allí, MSBuild no es lo suficientemente inteligente como para ver la biblioteca PCL utilizando el recurso. La acción de compilación = el contenido debería ser lo suficientemente bueno, como lo es para un instalador, pero eso no funciona. Sólo empaquetará la DLL, no el recurso. Se hizo para suponer que lo incrustaría, la mejor solución para la práctica.
Lo que tienes que hacer es anular el manifiesto del paquete. Descrito en este artículo de MSDN . 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.
Si ha agregado archivos como recursos, verifique su .Designer.cs, habrá una propiedad para cada recurso. Se puede acceder desde eso.
Aquí está la propiedad de generación automática de muestra para recursos de archivos de datos.
internal static byte[] MyDatFile {
get {
object obj = ResourceManager.GetObject("MyDatFile", resourceCulture);
return ((byte[])(obj));
}
Puedes leer el archivo dat como
System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
var str = enc.GetString(Resource1.MyDatFile);
Tu camino está mal. Está utilizando barras diagonales, pero en los nombres de recursos de manifiesto incrustados, las barras inclinadas se convirtieron en puntos durante la compilación. Además, dependiendo de las plataformas específicas de PCL, es posible que ni siquiera pueda llamar a Assembly.GetExecutingAssembly()
.
Esto es lo que puedes 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");
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);
}