GetManifestResourceStream restituisce NULL


105

Questa è un'applicazione C # .NET 4.0:

Sto incorporando un file di testo come risorsa e quindi provo a visualizzarlo in una finestra di dialogo:

    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 soluzione è MyProjSolution e l'eseguibile è MyProj.exe. Help.txt è una risorsa incorporata. Tuttavia, il flusso è nullo. Ho provato MyProjSolution.Help.txt e MyProjSolution.MyProj.Help.txt ma niente sembra funzionare.


1
Utilizzare ildasm.exe per esaminare i nomi .mresource nel manifesto dell'assembly. Evita di cadere in questo pozzo di miseria, usa invece Progetto + Proprietà, scheda Risorsa. Quindi puoi semplicemente usare Properties.Resources.Help nel tuo codice sorgente.
Hans Passant

Risposte:


189

È possibile verificare che le risorse siano incorporate correttamente utilizzando

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

durante il debug. Questo elencherà tutti i (nomi completi) di tutte le risorse incorporate nell'assembly in cui è scritto il codice.

Vedere Assembly.GetManifestResourceNames () su MSDN.

Copia semplicemente il nome pertinente e usalo al posto di quello che hai definito nella variabile "resourceName".

Note: il nome della risorsa distingue tra maiuscole e minuscole e se il file di risorse è stato incorporato in modo errato, non verrà visualizzato nell'elenco restituito dalla chiamata a GetManifestResourceNames (). Inoltre, assicurati di leggere la risorsa dall'assembly corretto (se vengono utilizzati più assembly): è fin troppo facile ottenere le risorse dall'assembly attualmente in esecuzione piuttosto che da un assembly di riferimento.

EDIT - .NET Core
Si prega di vedere questo post SO per i dettagli su come incorporare utilizzando .NET Core.

Il recupero delle informazioni sul manifesto sembra essere simile: basta usare this.GetType().GetTypeInfo().Assembly.GetManifestResourceNames()per ottenere il manifesto dall'assembly in cui è in esecuzione il codice.

Non ho ancora capito come eseguire l'equivalente di Assembly.GetExecutingAssembly().NET Core! se qualcuno lo sa, per favore fatemelo sapere e aggiornerò questa risposta.


5
Ha funzionato. ProjectName.Resources.Help.txt era il nome della risorsa incorporata.
Ron

2
Sono contento di aver aiutato! Se ritieni che questo post abbia risposto alla tua domanda, non dimenticare di contrassegnarlo come risposta accettata.
Jay

1
Sì, quindi in .Net Standard dovrebbe essere [ProjectName]. [Namespace]. [Resource]. Mi manca il nome del progetto. Grazie!
Billy Jake O'Connor

Il mio problema stava usando GetExecutingAssembly () invece di GetEntryAssembly (). La risorsa che volevo era nell'eseguibile, ma la funzione per caricare la risorsa risiedeva in un altro progetto referenziato nella stessa soluzione.
lattuga

63

Ho riscontrato un problema simile prima controlla che il file sia incluso nel tuo progetto, quindi vai alle proprietà e imposta l'azione di compilazione di quel file su Risorsa incorporata. questo ha funzionato per me.


Questo mi ha aiutato! Ho avuto alcuni file aggiunti in precedenza che erano predefiniti come risorsa incorporata, poi quelli aggiunti in seguito che erano impostati su "Nessuno". Visual Studio è così fastidioso a volte. La parte peggiore di questo è che tutti i file XML per le risorse NON hanno alcuna impostazione per l'azione di compilazione. Dovrebbe avere l'azione di compilazione impostata lì.
John Suit

1
Ricorda di utilizzare lo spazio dei nomi e il percorso della risorsa predefiniti. ad es DefatultNameSpace.Infrastructure.Help.txt. Spazio dei nomi predefinito nella pagina delle proprietà del progetto e Infrastructuresarebbe la cartella in cui si trova il file
jabu.hlong

Questo è quello che volevo Cheers!
tabby

19

La proprietà "Build Action" del file incorporato deve essere impostata come "Embedded Resource" per eseguire correttamente la riga, fornita di seguito:

Stream stream = assembly.GetManifestResourceStream(resourceName)

Fare clic con il pulsante destro del mouse sul file, fare clic sulla proprietà e quindi impostare la proprietà "Build Action" come "Embedded Resource":

inserisci qui la descrizione dell'immagine


Esattamente qual era il mio problema. Grazie!
Riki

1
Questo era il mio problema. L'ho aggiunto utilizzando Resources.resx e non ha funzionato.
Hélder Lima

11

Ecco la causa del mio valore nullo.

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

Il GetManifestResourceStreammetodo restituirà sempre NULLse la proprietà "azione costruita" della risorsa non è impostata su "risorsa incorporata"

Dopo aver impostato questa proprietà con tutti i file necessari, assembly.GetManifestResourceStreaminizia a restituire il flusso corretto invece di NULL.


1
Grazie ancora. Questo ha risolto il mio problema un mese o due fa, poi me ne sono dimenticato e ho avuto lo stesso problema e lo ha risolto di nuovo.
Rich

8

Solo un avvertimento.

Non ho potuto accedere al mio file come risorsa incorporata anche se ho specificato che lo era e anche se aveva quella proprietà Build Action. Ho sprecato un sacco di tempo a sbattere la testa. Ho incorporato un file di codice csharp con .txt aggiunto al suo nome (xxx.cs.txt). Per qualche motivo i metodi GetManifestResourceNames () e GetManifestResourceStream () non vedranno un file con .cs nel nome.

L'ho rinominato semplicemente xxx.txt ed è andato tutto bene.

Strano.


1
Questo ha portato via troppo tempo oggi! Lo incorporerà se lo usi Resourcema non Embedded Resource, il che lo rende ancora più strano ... Rimuoverlo .cs.dal nome lo fa funzionare. Argh.
Matt

Il problema non è che .cs. percorso / segmento nei file è riconosciuto come file C # ma piuttosto come CultureInfo.
frontlinebg

2
Sembra che con una doppia estensione non funzioni affatto.
Darion Badlydone

3

Ho avuto lo stesso problema, grazie a Jay ho scoperto che c'erano trattini nel nome della directory.

ProjectName.ResourceFolder.Sub-Directorydiventa ProjectName.ResourceFolder.Sub_Directoryquando si fa riferimento al flusso di risorse.


2

Nel mio caso il problema era che il codice che cercava la risorsa era in un progetto diverso rispetto alla risorsa stessa.

È possibile accedere solo alle risorse che si trovano nello stesso progetto del codice. Pensavo di poter mettere tutte le mie risorse nel progetto della pagina web, ma ho bisogno anche delle immagini nel progetto della posta.

Spero che questo aiuti qualcuno nella stessa situazione in cui mi trovavo.

Trovo la chiamata davvero utile Assembly.GetExecutingAssembly().GetManifestResourceNames();.


Questo non è vero, almeno dal 2011: sia tramite Assembly.LoadFrom () che tramite typeof si accede alle risorse che sono in un altro progetto
Marcelo Scofano

1

Nel caso in cui aiuti qualcun altro, assicurati che la Assembly.GetExecutingAssembly()linea venga chiamata dallo stesso assembly che ha risorse incorporate.


Tranne se lo chiami da un altro progetto, e in questo caso dovresti usare Assembly.LoadFrom () o typeof in modo da poter accedere alle risorse che sono in un altro progetto ...
Marcelo Scofano

1

Una soluzione semplice e snella è avere questa 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();
        }
    }
}

Quindi, quando aggiungi una risorsa, crei una classe C # del lettore nella stessa cartella:

inserisci qui la descrizione dell'immagine

dove la classe del lettore MyResource.cs è molto semplice:

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

Quindi, ogni risorsa avrà una classe "ombra" che sa come leggerla correttamente.

Ecco come leggi la risorsa nel tuo codice:

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

E come suggerito da altre risposte, non dimenticare di impostare "Risorsa incorporata" nella proprietà Azione di compilazione del file di risorse.

Il vantaggio di questa soluzione uniforme è

  1. meno problemi con la ricerca del nome completo corretto della risorsa, soprattutto se inserita in cartelle nidificate
  2. nel caso in cui quando la cartella viene rinominata OPPURE lo spazio dei nomi predefinito nelle impostazioni del progetto viene modificato, il codice NON si interromperà

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>

0

È necessario scaricare la soluzione, quindi modificare il progetto, dopo aver trovato la cartella e modificare in questo modo:

<EmbeddedResource Include="yourpath" />

0

Sebbene OP abbia ottenuto GetManifestResourceStream che restituisce NULL dalle risorse nello stesso assembly, alcune risposte suggeriscono che quando le risorse si trovano in un altro progetto o assembly non possono essere recuperate e sono una giusta causa per cui GetManifestResourceStream restituisce NULL.

Questo non è vero, almeno dal 2011; come ho indicato in alcuni commenti altrove, Assembly.LoadFrom () o typeof fanno il trucco e di conseguenza puoi accedere alle risorse che si trovano in un altro progetto.

Ho qui un esempio moderatamente complesso da illustrare; questa è la mia configurazione di prova:

inserisci qui la descrizione dell'immagine

Percorso a un altro progetto:

inserisci qui la descrizione dell'immagine

Catturato qui:

 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 su Form1.cs da WinFormFramework ho specificato con

Namespace.Folder.Resource

come quello:

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

E il risultato visualizzato nella casella di testo: inserisci qui la descrizione dell'immagine

Ho impiegato diverse ore per farlo bene; per questo, ho dovuto usarne molti in Finestra immediata:

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

Spero che aiuti qualcuno


-2

Probabilmente devi specificare il percorso del tuo file txt nel GetManifestResourceStreamparametro, oppure potresti provare a inserire il file txt nella stessa directory del tuo eseguibile. Spero che aiuti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.