GetManifestResourceStream gibt NULL zurück

105

Dies ist eine C # .NET 4.0-Anwendung:

Ich binde eine Textdatei als Ressource ein und versuche dann, sie in einem Dialogfeld anzuzeigen:

    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);
            }
        }

Die Lösung ist MyProjSolution und die ausführbare Datei ist MyProj.exe. Help.txt ist eine eingebettete Ressource. Der Stream ist jedoch null. Ich habe MyProjSolution.Help.txt und MyProjSolution.MyProj.Help.txt ausprobiert, aber nichts scheint zu funktionieren.

Ron
quelle
1
Verwenden Sie ildasm.exe, um die .mresource-Namen im Assembly-Manifest anzuzeigen. Vermeiden Sie es, in diese Grube des Elends zu geraten. Verwenden Sie stattdessen Projekt + Eigenschaften, Registerkarte Ressource. Sie können also einfach Properties.Resources.Help in Ihrem Quellcode verwenden.
Hans Passant

Antworten:

189

Sie können mithilfe von überprüfen, ob die Ressourcen korrekt eingebettet sind

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

beim Debuggen. Dadurch werden alle (vollständig qualifizierten Namen) aller Ressourcen aufgelistet, die in die Assembly eingebettet sind, in die Ihr Code geschrieben ist.

Siehe Assembly.GetManifestResourceNames () auf MSDN.

Kopieren Sie einfach den entsprechenden Namen und verwenden Sie diesen anstelle dessen, was Sie in der Variablen 'resourceName' definiert haben.

Hinweise - Der Ressourcenname unterscheidet zwischen Groß- und Kleinschreibung. Wenn Sie die Ressourcendatei falsch eingebettet haben, wird sie nicht in der Liste angezeigt, die vom Aufruf von GetManifestResourceNames () zurückgegeben wird. Stellen Sie außerdem sicher, dass Sie die Ressource aus der richtigen Assembly lesen (wenn mehrere Assemblys verwendet werden). Es ist allzu einfach, die Ressourcen aus der aktuell ausgeführten Assembly und nicht aus einer Assembly zu beziehen, auf die verwiesen wird.

BEARBEITEN - .NET Core In
diesem SO-Beitrag finden Sie Details zum Einbetten mit .NET Core.

Das Abrufen der Manifestinformationen sieht ähnlich aus. Verwenden Sie this.GetType().GetTypeInfo().Assembly.GetManifestResourceNames()diese Option, um das Manifest von der Assembly abzurufen, in der der Code ausgeführt wird.

Ich habe noch nicht herausgefunden, wie man das Äquivalent zu Assembly.GetExecutingAssembly().NET Core macht! Wenn jemand weiß - bitte lassen Sie es mich wissen und ich werde diese Antwort aktualisieren.

Jay
quelle
5
Das hat funktioniert. ProjectName.Resources.Help.txt war der Name der eingebetteten Ressource.
Ron
2
Ich bin froh, geholfen zu haben! Wenn Sie der Meinung sind, dass dieser Beitrag Ihre Frage beantwortet hat, vergessen Sie bitte nicht, dies als akzeptierte Antwort zu markieren.
Jay
1
Ja, also in .Net Standard sollte es [Projektname]. [Namespace]. [Ressource] sein. Ich vermisse den Projektnamen. Vielen Dank!
Billy Jake O'Connor
Mein Problem war die Verwendung von GetExecutingAssembly () anstelle von GetEntryAssembly (). Die Ressource, die ich wollte, befand sich in der ausführbaren Datei, aber die Funktion zum Laden der Ressource befand sich in einem anderen Projekt, auf das verwiesen wurde, in derselben Lösung.
Lettucemode
63

Bei einem ähnlichen Problem wurde zuerst überprüft, ob die Datei in Ihrem Projekt enthalten ist. Gehen Sie dann zu den Eigenschaften und setzen Sie die Erstellungsaktion dieser Datei auf Eingebettete Ressource. das hat bei mir funktioniert.

Jithesh Chandra
quelle
Das hat mir geholfen! Ich hatte früher einige Dateien hinzugefügt, die standardmäßig eingebettete Ressourcen waren, und später Dateien, die auf "Keine" gesetzt waren. Visual Studio ist manchmal so nervig. Das Schlimmste daran ist, dass alle XML-Dateien für Ressourcen KEINE Einstellung für Build-Aktionen haben. Sollte die Build-Aktion dort eingestellt sein.
John Suit
1
Denken Sie daran, den Standard-Namespace und den Pfad zur Ressource zu verwenden. zB DefatultNameSpace.Infrastructure.Help.txt. Standard-Namespace auf Ihrer Projekteigenschaftenseite und Infrastructurewäre der Ordner, in dem Sie sich befinden
jabu.hlong
Das wollte ich Prost!
Tabby
18

Die Eigenschaft "Build Action" der eingebetteten Datei sollte als "Embedded Resource" festgelegt werden, damit die unten angegebene Zeile ordnungsgemäß ausgeführt wird:

Stream stream = assembly.GetManifestResourceStream(resourceName)

Klicken Sie mit der rechten Maustaste auf die Datei, klicken Sie auf die Eigenschaft und legen Sie die Eigenschaft "Build Action" als "Embedded Resource" fest:

Geben Sie hier die Bildbeschreibung ein

Ozlu
quelle
Genau das war mein Problem. Vielen Dank!
Riki
1
Das war mein Problem. Ich habe es mit Resources.resx hinzugefügt und es hat nicht funktioniert.
Hélder Lima
11

Hier ist die Ursache für meinen Nullwert.

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

Die GetManifestResourceStreamMethode wird immer zurückgegeben, NULLwenn die Eigenschaft "Erstellte Aktion" der Ressource nicht auf "Eingebettete Ressource" festgelegt ist.

Nachdem Sie diese Eigenschaft mit allen erforderlichen Dateien festgelegt haben, wird assembly.GetManifestResourceStreamstattdessen der richtige Stream zurückgegeben NULL.

Nate
quelle
1
Danke noch einmal. Dies hat mein Problem vor ein oder zwei Monaten behoben, dann habe ich es vergessen und hatte das gleiche Problem und es hat es erneut behoben.
Rich
8

Nur eine Warnung.

Ich konnte nicht auf meine Datei als eingebettete Ressource zugreifen, obwohl ich angegeben habe, dass dies der Fall ist und obwohl diese Build Action-Eigenschaft vorhanden ist. Ich habe viel Zeit damit verschwendet, meinen Kopf zu schlagen. Ich habe eine csharp-Codedatei eingebettet, an deren Namen (xxx.cs.txt) .txt angehängt ist. Aus irgendeinem Grund sehen die Methoden GetManifestResourceNames () und GetManifestResourceStream () keine Datei mit .cs im Namen.

Ich habe es einfach in xxx.txt umbenannt und alles war in Ordnung.

Seltsam.

Belmiris
quelle
1
Das hat heute zu viel Zeit gekostet! Es wird eingebettet, wenn Sie es verwenden, Resourceaber nicht Embedded Resource, was es noch seltsamer macht ... Wenn Sie es .cs.aus dem Namen entfernen , funktioniert es. Argh.
Matt
Das Problem ist nicht, dass .cs. Pfad / Segment in Dateien wird als C # -Datei erkannt, jedoch als CultureInfo.
Frontlinebg
2
Es scheint, dass es mit einer doppelten Erweiterung überhaupt nicht funktioniert.
Darion Badlydone
3

Ich hatte das gleiche Problem, dank Jay fand ich, dass es Bindestriche im Verzeichnisnamen waren.

ProjectName.ResourceFolder.Sub-Directorywird, ProjectName.ResourceFolder.Sub_Directorywenn Sie auf den Ressourcenstrom verweisen.

Aaron
quelle
2

In meinem Fall bestand das Problem darin, dass sich der Code, der nach der Ressource suchte, in einem anderen Projekt befand als die Ressource selbst.

Sie können nur auf Ressourcen zugreifen, die sich in demselben Projekt befinden, in dem sich der Code befindet. Ich dachte, ich könnte alle meine Ressourcen in das Webseitenprojekt einfügen, aber ich brauche auch Bilder im E-Mail-Projekt.

Hoffe das hilft jemandem in der gleichen Situation wie ich.

Ich finde wirklich nützliche Anrufe Assembly.GetExecutingAssembly().GetManifestResourceNames();.

AxelWass
quelle
Dies gilt zumindest seit 2011 nicht mehr: Über Assembly.LoadFrom () und typeof können Sie auf Ressourcen zugreifen, die sich in einem anderen Projekt befinden
Marcelo Scofano
1

Wenn es anderen hilft, stellen Sie sicher, dass die Assembly.GetExecutingAssembly()Leitung von derselben Assembly aufgerufen wird, in die Ressourcen eingebettet sind.

Krish
quelle
Außer wenn Sie es aus einem anderen Projekt aufrufen und in diesem Fall Assembly.LoadFrom () oder typeof verwenden sollten, damit Sie auf Ressourcen zugreifen können, die sich in einem anderen Projekt befinden ...
Marcelo Scofano
1

Eine einfache und optimierte Lösung besteht darin, diese Basisklasse zu haben :

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();
        }
    }
}

Wenn Sie dann eine Ressource hinzufügen, erstellen Sie eine C # -Klasse für Leser im selben Ordner:

Geben Sie hier die Bildbeschreibung ein

wo die Leserklasse MyResource.cs sehr einfach ist:

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

Jede Ressource verfügt also über eine "Schatten" -Klasse, die weiß, wie man sie richtig liest.

So lesen Sie die Ressource in Ihrem Code:

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

Vergessen Sie nicht, wie in anderen Antworten vorgeschlagen, "Eingebettete Ressource" in der Eigenschaft "Aktion erstellen" der Ressourcendatei festzulegen.

Der Vorteil dieser einheitlichen Lösung ist

  1. Es ist weniger mühsam, den richtigen vollständigen Namen der Ressource zu finden, insbesondere wenn diese in verschachtelten Ordnern abgelegt ist
  2. Falls der Ordner umbenannt wird ODER der Standard-Namespace in den Projekteinstellungen geändert wird, wird der Code NICHT unterbrochen
VeganHunter
quelle
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>
Geek
quelle
0

Sie müssen Ihre Lösung entladen. Bearbeiten Sie dann das Projekt. Suchen Sie Ihren Ordner und ändern Sie ihn wie folgt:

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

Obwohl OP GetManifestResourceStream dazu gebracht hat, NULL von Ressourcen in derselben Assembly zurückzugeben, wurde in einigen Antworten darauf hingewiesen, dass Ressourcen in einem anderen Projekt oder einer anderen Assembly nicht abgerufen werden können und eine gute Ursache dafür sind, dass GetManifestResourceStream NULL zurückgibt.

Dies gilt zumindest seit 2011 nicht mehr; Wie ich in einigen Kommentaren an anderer Stelle erwähnt habe, machen Assembly.LoadFrom () oder typeof den Trick und als Ergebnis können Sie auf Ressourcen zugreifen, die sich in einem anderen Projekt befinden.

Ich habe hier ein mäßig komplexes Beispiel zu veranschaulichen; Das ist mein Testaufbau:

Geben Sie hier die Bildbeschreibung ein

Weg zu einem anderen Projekt:

Geben Sie hier die Bildbeschreibung ein

Hier aufgenommen:

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

Und auf Form1.cs von WinFormFramework spezifiziere ich mit

Namespace.Folder.Resource

so wie das:

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

Und das Ergebnis wird im Textfeld angezeigt: Geben Sie hier die Bildbeschreibung ein

Ich habe mehrere Stunden damit verbracht, es richtig zu machen. Dafür musste ich bei Immediate Window viel verwenden:

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

Hoffe es hilft jemandem

Marcelo Scofano
quelle
-2

Möglicherweise müssen Sie den Pfad zu Ihrer txt-Datei im GetManifestResourceStreamParameter angeben , oder Sie können versuchen, die txt-Datei im selben Verzeichnis wie Ihre ausführbare Datei zu speichern. Hoffentlich hilft das!

Miles Watson
quelle