GetManifestResourceStream retorna NULL

105

Este é um aplicativo C # .NET 4.0:

Estou incorporando um arquivo de texto como um recurso e, em seguida, tentando exibi-lo em uma caixa 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);
            }
        }

A solução é MyProjSolution e o executável é MyProj.exe. Help.txt é um recurso incorporado. No entanto, o fluxo é nulo. Tentei MyProjSolution.Help.txt e MyProjSolution.MyProj.Help.txt, mas nada parece funcionar.

Ron
fonte
1
Use ildasm.exe para examinar os nomes .mresource no manifesto do assembly. Evite cair neste poço de miséria, use Projeto + Propriedades, guia Recurso. Portanto, você pode apenas usar Properties.Resources.Help em seu código-fonte.
Hans Passant

Respostas:

189

Você pode verificar se os recursos estão incorporados corretamente usando

//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()

ao depurar. Isso listará todos os (nomes totalmente qualificados) de todos os recursos incorporados no assembly em que seu código está escrito

Consulte Assembly.GetManifestResourceNames () no MSDN.

Simplesmente copie o nome relevante e use-o em vez de tudo o que você definiu na variável 'resourceName'.

Observações - o nome do recurso diferencia maiúsculas de minúsculas e se você incorporou incorretamente o arquivo de recurso, ele não aparecerá na lista retornada pela chamada para GetManifestResourceNames (). Além disso - certifique-se de ler o recurso do assembly correto (se vários assemblies forem usados) - é muito fácil obter os recursos do assembly em execução no momento em vez de um assembly referenciado.

EDITAR - .NET Core
Consulte esta postagem do SO para obter detalhes sobre como incorporar usando o .NET Core.

A recuperação das informações do manifesto parece ser semelhante - use apenas this.GetType().GetTypeInfo().Assembly.GetManifestResourceNames()para obter o manifesto do assembly onde o código está sendo executado.

Eu não descobri como fazer o equivalente Assembly.GetExecutingAssembly()no .NET Core ainda! se alguém souber - avise-me e atualizarei esta resposta.

Jay
fonte
5
Isso funcionou. ProjectName.Resources.Help.txt era o nome do recurso incorporado.
Ron
2
Estou feliz por ter ajudado! Se você acha que esta postagem respondeu à sua pergunta, não se esqueça de marcar como a resposta aceita.
Jay
1
Sim, então no .Net Standard deve ser [ProjectName]. [Namespace]. [Resource]. Estou faltando o nome do projeto. Obrigado!
Billy Jake O'Connor,
Meu problema era usar GetExecutingAssembly () em vez de GetEntryAssembly (). O recurso que eu queria estava no executável, mas a função de carregar o recurso residia em outro projeto referenciado na mesma solução.
lettucemode
63

Tive um problema semelhante, verifiquei primeiro se o arquivo está incluído em seu projeto e, em seguida, acesse as propriedades e defina a ação de construção desse arquivo como Embedded Resource. isso funcionou para mim.

Jithesh Chandra
fonte
Isso me ajudou! Eu tinha alguns arquivos adicionados anteriormente que tinham como padrão o recurso incorporado, depois alguns que adicionei mais tarde que foram definidos como "Nenhum". O Visual Studio é tão irritante às vezes. A pior parte disso é que todos os arquivos XML para recursos NÃO têm configuração para ação de construção. Deve ter a ação de construção definida lá.
John Suit
1
Lembre-se de usar o namespace padrão e o caminho para o recurso. por exemplo DefatultNameSpace.Infrastructure.Help.txt. Espaço de nome padrão na página de propriedades do projeto e Infrastructureseria a pasta em que seu arquivo está
jabu.hlong
Isso é o que eu queria Cheers!
gato malhado de
19

A propriedade "Build Action" do arquivo incorporado deve ser definida como "Embedded Resource" para executar a linha, que é fornecida a seguir, corretamente:

Stream stream = assembly.GetManifestResourceStream(resourceName)

Clique com o botão direito no arquivo, clique na propriedade e defina a propriedade "Build Action" como "Embedded Resource":

insira a descrição da imagem aqui

Ozlu
fonte
Qual era exatamente o meu problema. Obrigado!
Riki
1
Esse era o meu problema. Eu adicionei usando Resources.resx e não funcionou.
Hélder Lima
11

Aqui está a causa do meu valor nulo.

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

O GetManifestResourceStreammétodo sempre retornará NULLse a propriedade 'ação construída' do recurso não estiver configurada para 'recurso incorporado'

Depois de definir essa propriedade com todos os arquivos necessários, assembly.GetManifestResourceStreamcomeça a retornar o fluxo correto em vez de NULL.

Nate
fonte
1
Obrigado novamente. Isso corrigiu meu problema um ou dois meses atrás, então eu esqueci e tive o mesmo problema e ele corrigiu novamente.
Rico,
8

Apenas um aviso.

Não consegui acessar meu arquivo como um recurso incorporado, embora tenha especificado que era e mesmo que tivesse a propriedade Build Action. Perdi muito tempo batendo minha cabeça. Eu embuti um arquivo de código csharp com .txt anexado ao seu nome (xxx.cs.txt). Por alguma razão, os métodos GetManifestResourceNames () e GetManifestResourceStream () não verão um arquivo com .cs em seu nome.

Mudei o nome simplesmente xxx.txt e estava tudo bem.

Esquisito.

Belmiris
fonte
1
Isso esgotou muito tempo hoje! Ele irá incorporá-lo se você usar, Resourcemas não Embedded Resource, o que o torna ainda mais estranho ... Remover .cs.do nome faz com que funcione. Argh.
Matt
O problema não é esse .cs. caminho / segmento em arquivos é reconhecido como arquivo C #, mas sim como CultureInfo.
frontlinebg
2
Parece que com uma extensão dupla não funciona de todo.
Darion Badlydone
3

Eu tive o mesmo problema, graças ao Jay descobri que havia hífens no nome do diretório.

ProjectName.ResourceFolder.Sub-Directorytorna-se ProjectName.ResourceFolder.Sub_Directoryquando você faz referência ao fluxo de recursos.

Aaron
fonte
2

No meu caso, o problema era que o código que procurava o recurso estava em um projeto diferente do próprio recurso.

Você só pode acessar recursos que estão no mesmo projeto que o código está. Achei que poderia colocar todos os meus recursos no projeto de página da web, mas também preciso de imagens no projeto de e-mail.

Espero que isso ajude alguém na mesma situação que eu.

Acho uma chamada muito útil Assembly.GetExecutingAssembly().GetManifestResourceNames();.

AxelWass
fonte
Isso não é verdade, pelo menos desde 2011: através de Assembly.LoadFrom () e typeof você pode acessar recursos que estão em outro projeto
Marcelo Scofano
1

Caso ajude mais alguém, certifique-se de que a Assembly.GetExecutingAssembly()linha seja chamada a partir do mesmo conjunto que possui recursos incorporados.

Krish
fonte
Exceto se você chamar de outro projeto, e neste caso você deve usar Assembly.LoadFrom () ou typeof para que você possa acessar recursos que estão em outro projeto ...
Marcelo Scofano 01/08
1

Uma solução simples e simplificada é ter esta classe 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();
        }
    }
}

Então, ao adicionar um recurso, você cria uma classe de leitura C # na mesma pasta:

insira a descrição da imagem aqui

onde a classe do leitor MyResource.cs é muito simples:

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

Assim, cada recurso terá uma classe "sombra" que saberá como lê-lo corretamente.

É assim que você lê o recurso em seu código:

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

E como outras respostas sugeridas, não se esqueça de definir "Embedded Resource" na propriedade Build Action do arquivo de recurso.

A vantagem desta solução uniforme é

  1. menos complicações para encontrar o nome completo correto do recurso, especialmente quando colocado em pastas aninhadas
  2. no caso de a pasta ser renomeada OU o namespace padrão nas configurações do projeto for alterado, o código NÃO quebrará
VeganHunter
fonte
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>
Nerd
fonte
0

Você precisa descarregar sua solução. Em seguida, edite o projeto. Depois de encontrar sua pasta e alterar assim:

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

Embora OP obtivesse GetManifestResourceStream retornando NULL de recursos no mesmo assembly, algumas respostas sugeriram que quando os recursos estão em outro projeto ou assembly eles não podem ser recuperados e são uma causa justa de GetManifestResourceStream retornar NULL.

Isso não é verdade, pelo menos desde 2011; como apontei em alguns comentários em outro lugar, Assembly.LoadFrom () ou typeof fazem o truque e, como resultado, você pode acessar recursos que estão em outro projeto.

Tenho um exemplo moderadamente complexo aqui para ilustrar; esta é minha configuração de teste:

insira a descrição da imagem aqui

Caminho para outro projeto:

insira a descrição da imagem aqui

Capturado aqui:

 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";

E em Form1.cs de WinFormFramework, eu especifico com

Namespace.Folder.Resource

Curtiu isso:

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

E o resultado exibido na caixa de texto: insira a descrição da imagem aqui

Passei várias horas para acertar; para isso, tive que usar muito estes na janela imediata:

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

Espero que ajude alguém

Marcelo Scofano
fonte
-2

Você provavelmente precisa especificar o caminho para o seu arquivo txt no GetManifestResourceStreamparâmetro, ou você pode tentar colar o arquivo txt no mesmo diretório do seu executável. Espero que ajude!

Miles Watson
fonte