GetManifestResourceStream devuelve NULL

105

Esta es una aplicación C # .NET 4.0:

Estoy incrustando un archivo de texto como recurso y luego intento mostrarlo en un cuadro de diálogo:

    var assembly = Assembly.GetExecutingAssembly();
    var resourceName = "MyProj.Help.txt";

        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
        {
            using (StreamReader reader = new StreamReader(stream))
            {
                string result = reader.ReadToEnd();
                System.Windows.Forms.MessageBox.Show(result, "MyProj", MessageBoxButtons.OK);
            }
        }

La solución es MyProjSolution y el ejecutable es MyProj.exe. Help.txt es un recurso integrado. Sin embargo, la secuencia es nula. Probé MyProjSolution.Help.txt y MyProjSolution.MyProj.Help.txt pero nada parece funcionar.

Ron
fuente
1
Utilice ildasm.exe para ver los nombres de .mresource en el manifiesto del ensamblado. Evite caer en este pozo de la miseria, use Proyecto + Propiedades, pestaña Recursos en su lugar. Entonces puede usar Properties.Resources.Help en su código fuente.
Hans Passant

Respuestas:

189

Puede comprobar que los recursos están incrustados correctamente utilizando

//From the assembly where this code lives!
this.GetType().Assembly.GetManifestResourceNames()

//or from the entry point to the application - there is a difference!
Assembly.GetExecutingAssembly().GetManifestResourceNames()

al depurar. Esto enumerará todos los (nombres completos) de todos los recursos incrustados en el ensamblado en el que está escrito su código.

Vea Assembly.GetManifestResourceNames () en MSDN.

Simplemente copie el nombre relevante y utilícelo en lugar de lo que haya definido en la variable 'resourceName'.

Notas: el nombre del recurso distingue entre mayúsculas y minúsculas, y si ha incrustado incorrectamente el archivo de recursos, no aparecerá en la lista devuelta por la llamada a GetManifestResourceNames (). Además, asegúrese de leer el recurso del ensamblado correcto (si se utilizan varios ensamblados); es muy fácil obtener los recursos del ensamblado que se está ejecutando actualmente en lugar de un ensamblado referenciado.

EDITAR: .NET Core
Consulte esta publicación de SO para obtener detalles sobre cómo incrustar usando .NET Core.

Recuperar la información del manifiesto parece ser similar; solo utilícelo this.GetType().GetTypeInfo().Assembly.GetManifestResourceNames()para obtener el manifiesto del ensamblado donde se está ejecutando el código.

¡Todavía no he descubierto cómo hacer el equivalente Assembly.GetExecutingAssembly()en .NET Core! si alguien lo sabe, hágamelo saber y actualizaré esta respuesta.

Arrendajo
fuente
5
Eso funciono. ProjectName.Resources.Help.txt era el nombre del recurso incrustado.
Ron
2
¡Me alegro de haber ayudado! Si cree que esta publicación ha respondido a su pregunta, no olvide marcarla como la respuesta aceptada.
Jay
1
Sí, en .Net Standard debería ser [ProjectName]. [Namespace]. [Resource]. Me falta el nombre del proyecto. ¡Gracias!
Billy Jake O'Connor
Mi problema fue usar GetExecutingAssembly () en lugar de GetEntryAssembly (). El recurso que quería estaba en el ejecutable, pero la función para cargar el recurso vivía en otro proyecto referenciado en la misma solución.
lechuga
63

Tuve un problema similar, verifique primero que el archivo esté incluido en su proyecto, luego vaya a propiedades y establezca la acción de compilación de ese archivo en Recurso incrustado. esto funcionó para mí.

Jithesh Chandra
fuente
¡Esto me ayudó! Tenía algunos archivos agregados anteriormente que estaban predeterminados como recurso incrustado, luego agregué más tarde que se establecieron en "Ninguno". Visual Studio es muy molesto a veces. La peor parte de esto es que todos los archivos XML para recursos NO tienen configuración para la acción de compilación. Debería tener la acción de compilación establecida allí.
John Suit
1
Recuerde utilizar el espacio de nombres predeterminado y la ruta al recurso. ej DefatultNameSpace.Infrastructure.Help.txt. Espacio de nombre predeterminado en la página de propiedades de su proyecto y Infrastructuresería la carpeta en la que se encuentra el archivo
jabu.hlong
Esto es lo que quería ¡Saludos!
atigrado
19

La propiedad "Acción de compilación" del archivo incrustado debe establecerse como "Recurso incrustado" para ejecutar la línea, que se muestra a continuación, correctamente:

Stream stream = assembly.GetManifestResourceStream(resourceName)

Haga clic derecho en el archivo, haga clic en la propiedad y luego establezca la propiedad "Acción de compilación" como "Recurso incrustado":

ingrese la descripción de la imagen aquí

Ozlu
fuente
Exactamente cuál era mi problema. ¡Gracias!
Riki
1
Este era mi problema. Lo agregué usando Resources.resx y no funcionó.
Hélder Lima
11

Aquí está la causa de mi valor nulo.

http://adrianmejia.com/blog/2011/07/18/cs-getmanifestresourcestream-gotcha/

El GetManifestResourceStreammétodo siempre devolverá NULLsi la propiedad 'acción construida' del recurso no está establecida en 'recurso incrustado'

Después de establecer esta propiedad con todos los archivos necesarios, assembly.GetManifestResourceStreamcomienza a devolver la secuencia correcta en lugar de NULL.

Nate
fuente
1
Gracias de nuevo. Esto solucionó mi problema hace uno o dos meses, luego lo olvidé y tuve el mismo problema y lo solucionó nuevamente.
Rich
8

Sólo una advertencia.

No pude acceder a mi archivo como recurso incrustado aunque especifiqué que lo era y aunque tenía esa propiedad Build Action. Perdí mucho tiempo golpeándome la cabeza. Inserté un archivo de código csharp con .txt adjunto a su nombre (xxx.cs.txt). Por alguna razón, los métodos GetManifestResourceNames () y GetManifestResourceStream () no verán un archivo con .cs en su nombre.

Lo renombré simplemente xxx.txt y todo estuvo bien.

Extraño.

Belmiris
fuente
1
¡Esto ha agotado demasiado tiempo hoy! Lo incrustará si lo usa Resourcepero no Embedded Resource, lo que lo hace aún más extraño ... Quitar .cs.del nombre lo hace funcionar. Argh.
Matt
El problema no es que .cs. La ruta / segmento en archivos se reconoce como archivo C #, sino más bien como CultureInfo.
frontlinebg
2
Parece que con una doble extensión no funciona en absoluto.
Darion Badlydone
3

Tuve el mismo problema, gracias a Jay descubrí que había guiones en el nombre del directorio.

ProjectName.ResourceFolder.Sub-Directoryse convierte en ProjectName.ResourceFolder.Sub_Directorycuando hace referencia al flujo de recursos.

Aaron
fuente
2

En mi caso, el problema fue que el código que buscaba el recurso estaba en un proyecto diferente al del propio recurso.

Solo puede acceder a los recursos que están en el mismo proyecto que el código. Pensé que podría poner todos mis recursos en el proyecto de la página web, pero también necesito imágenes en el proyecto de correo.

Espero que esto ayude a alguien en la misma situación que yo.

Encuentro llamadas realmente útiles Assembly.GetExecutingAssembly().GetManifestResourceNames();.

AxelWass
fuente
Esto no es cierto, al menos desde 2011: a través de Assembly.LoadFrom () y typeof se puede acceder a recursos que están en otro proyecto
Marcelo Scofano
1

En caso de que ayude a alguien más, asegúrese Assembly.GetExecutingAssembly()de que se llame a la línea desde el mismo ensamblado que tiene recursos integrados.

Krish
fuente
Excepto si lo llamas desde otro proyecto, y en este caso debes usar Assembly.LoadFrom () o typeof para poder acceder a recursos que están en otro proyecto ...
Marcelo Scofano
1

Una solución simple y optimizada es tener esta clase base :

public class EmbededResourceReader
{
    protected string LoadString(string fileName)
    {
        return LoadString(fileName, Encoding.UTF8);
    }

    protected string LoadString(string fileName, Encoding encoding)
    {
        var assembly = this.GetType().Assembly;
        var resourceStream = assembly.GetManifestResourceStream($"{this.GetType().Namespace}.{fileName}");
        using (var reader = new StreamReader(resourceStream, encoding))
        {
            return reader.ReadToEnd();
        }
    }
}

Luego, cuando agrega un recurso, crea una clase de lector C # en la misma carpeta:

ingrese la descripción de la imagen aquí

donde la clase de lector MyResource.cs es muy simple:

public class MyResource : EmbededResourceReader
{
    public string LoadString() => LoadString($"{nameof(MyResource)}.txt");
}

Entonces, cada recurso tendrá una clase "sombra" que sepa leerlo correctamente.

Así es como lee el recurso en su código:

var text = new MyResource().LoadString();

Y como sugirieron otras respuestas, no olvide establecer "Recurso incrustado" en la propiedad Acción de compilación del archivo de recursos.

La ventaja de esta solución uniforme es

  1. menos problemas para encontrar el nombre completo correcto del recurso, especialmente cuando se coloca en carpetas anidadas
  2. en caso de que se cambie el nombre de la carpeta O se cambie el espacio de nombres predeterminado en la configuración del proyecto, el código NO se romperá
VeganHunter
fuente
0
    First Unload the project and click on edit the project file. 

    Inside the project file make sure that the item you are fetching from the assembly is included inside <EmbeddedResource> tag.

    Eg: 

         <ItemGroup>
          <EmbeddedResource Include="Template\ForExampleFile.html" />
         </ItemGroup>


    The files I added into the project were just in Content tag but not in the EmbeddedResource as shown below by default. Hence the stream was returning null.
    <ItemGroup>
        <Content Include="Template\ForExampleFile.html" />
  </ItemGroup>
Friki
fuente
0

Necesitas descargar tu solución, luego edita el proyecto, luego encuentra tu carpeta y cambia así:

<EmbeddedResource Include="yourpath" />
Tiga
fuente
0

Aunque OP obtuvo GetManifestResourceStream devolviendo NULL de los recursos en el mismo ensamblado, algunas respuestas sugirieron que cuando los recursos están en otro proyecto o ensamblado no se pueden recuperar y son una causa justa de que GetManifestResourceStream devuelva NULL.

Esto no es cierto, al menos desde 2011; como señalé en algunos comentarios en otro lugar, Assembly.LoadFrom () o typeof hacen el truco y, como resultado, puede acceder a los recursos que están en otro proyecto.

Tengo aquí un ejemplo moderadamente complejo para ilustrar; esta es mi configuración de prueba:

ingrese la descripción de la imagen aquí

Camino a otro proyecto:

ingrese la descripción de la imagen aquí

Capturado aquí:

 var sharedXMLResource =
                "D:\\My Documents\\Consultório Impressos\\DB Pacientes\\Teste\\TestesVariados\\WinFormFramework\\Read_Embedded_XML_File_CS\\bin\\Debug\\Read_Embedded_XML_File_CS.exe";

Y en Form1.cs de WinFormFramework especifico con

Namespace.Folder.Resource

como eso:

StreamReader reader = 
                new StreamReader(Assembly.LoadFrom(sharedXMLResource).GetManifestResourceStream("Read_Embedded_XML_File_CS.SharedResources.ContactList.xml") ?? throw new InvalidOperationException());

Y el resultado se muestra en el cuadro de texto: ingrese la descripción de la imagen aquí

Pasé varias horas para hacerlo bien; para eso, tuve que usar mucho estos en Immediate Window:

Environment.CurrentDirectory
AppDomain.CurrentDomain.BaseDirectory
System.Reflection.Assembly.GetExecutingAssembly().Location
System.Reflection.Assembly.GetAssembly(typeof(WinFormFramework.Program)).Location

Espero que ayude a alguien

Marcelo Scofano
fuente
-2

Probablemente necesite especificar la ruta a su archivo txt en el GetManifestResourceStreamparámetro, o podría intentar pegar el archivo txt en el mismo directorio que su ejecutable. ¡Espero que ayude!

Miles Watson
fuente