GetManifestResourceStream renvoie NULL

105

Ceci est une application C # .NET 4.0:

J'intègre un fichier texte en tant que ressource, puis j'essaie de l'afficher dans une boîte de dialogue:

    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 solution est MyProjSolution et l'exécutable est MyProj.exe. Help.txt est une ressource intégrée. Cependant, le flux est nul. J'ai essayé MyProjSolution.Help.txt et MyProjSolution.MyProj.Help.txt mais rien ne semble fonctionner.

Ron
la source
1
Utilisez ildasm.exe pour consulter les noms .mresource dans le manifeste d'assembly. Évitez de tomber dans ce gouffre de misère, utilisez plutôt Projet + Propriétés, onglet Ressource. Vous pouvez donc simplement utiliser Properties.Resources.Help dans votre code source.
Hans Passant

Réponses:

189

Vous pouvez vérifier que les ressources sont correctement incorporées en utilisant

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

lors du débogage. Cela listera toutes les (noms complets) de toutes les ressources incorporées dans l'assembly dans lequel votre code est écrit.

Voir Assembly.GetManifestResourceNames () sur MSDN.

Copiez simplement le nom pertinent et utilisez-le à la place de ce que vous avez défini dans la variable 'resourceName'.

Remarques - le nom de la ressource est sensible à la casse, et si vous avez incorrectement incorporé le fichier de ressources, il n'apparaîtra pas dans la liste renvoyée par l'appel à GetManifestResourceNames (). Assurez-vous également que vous lisez la ressource à partir de l'assembly approprié (si plusieurs assemblys sont utilisés) - il est trop facile d'obtenir les ressources de l'assembly en cours d'exécution plutôt que d'un assembly référencé.

EDIT - .NET Core
Veuillez consulter ce post SO pour plus de détails sur la façon d'intégrer à l'aide de .NET Core.

La récupération des informations du manifeste semble similaire - utilisez simplement this.GetType().GetTypeInfo().Assembly.GetManifestResourceNames()pour obtenir le manifeste a de l'assembly où le code est en cours d'exécution.

Je n'ai pas encore trouvé comment faire l'équivalent de Assembly.GetExecutingAssembly().NET Core! si quelqu'un sait - s'il vous plaît laissez-moi savoir et je mettrai à jour cette réponse.

Geai
la source
5
Ça a marché. ProjectName.Resources.Help.txt était le nom de la ressource incorporée.
Ron
2
Je suis content d'avoir aidé! Si vous pensez que ce message a répondu à votre question, n'oubliez pas de le marquer comme réponse acceptée.
Jay
1
Oui, donc dans .Net Standard, il devrait s'agir de [ProjectName]. [Namespace]. [Resource]. Je manque le nom du projet. Merci!
Billy Jake O'Connor
Mon problème était d'utiliser GetExecutingAssembly () au lieu de GetEntryAssembly (). La ressource que je souhaitais était dans l'exécutable, mais la fonction de chargement de la ressource vivait dans un autre projet référencé dans la même solution.
lettucemode
63

J'ai eu un problème similaire, vérifiez d'abord que le fichier est inclus dans votre projet, puis accédez aux propriétés et définissez l'action de construction de ce fichier sur Ressource intégrée. cela a fonctionné pour moi.

Jithesh Chandra
la source
Cela m'a aidé! J'ai eu quelques fichiers ajoutés plus tôt qui étaient par défaut une ressource intégrée, puis ceux que j'ai ajoutés plus tard qui étaient réglés sur "Aucun". Visual Studio est parfois si ennuyeux. Le pire dans tout cela est que tous les fichiers XML pour les ressources n'ont AUCUN paramètre pour l'action de génération. Devrait avoir l'action de construction définie ici.
John Suit le
1
N'oubliez pas d'utiliser l'espace de noms par défaut et le chemin d'accès à la ressource. par exemple DefatultNameSpace.Infrastructure.Help.txt. L'espace de noms par défaut dans la page de propriétés de votre projet et Infrastructureserait le dossier dans
lequel se
C'est ce que je voulais Cheers!
tabby
19

La propriété "Build Action" du fichier incorporé doit être définie sur "Embedded Resource" pour exécuter correctement la ligne indiquée ci-dessous:

Stream stream = assembly.GetManifestResourceStream(resourceName)

Faites un clic droit sur le fichier, cliquez sur la propriété, puis définissez la propriété "Action de construction" sur "Ressource intégrée":

entrez la description de l'image ici

Ozlu
la source
Quel était exactement mon problème. Merci!
Riki du
1
C'était mon problème. Je l'ai ajouté en utilisant Resources.resx et cela n'a pas fonctionné.
Hélder Lima
11

Voici la cause de ma valeur nulle.

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

La GetManifestResourceStreamméthode retournera toujours NULLsi la propriété 'action construite' de la ressource n'est pas définie sur 'ressource intégrée'

Après avoir défini cette propriété avec tous les fichiers nécessaires, assembly.GetManifestResourceStreamcommencez à renvoyer le flux correct au lieu de NULL.

Nate
la source
1
Merci encore. Cela a résolu mon problème il y a un mois ou deux, puis je l'ai oublié et j'ai eu le même problème et il l'a encore résolu.
Riche
8

Juste un avertissement.

Je ne pouvais pas accéder à mon fichier en tant que ressource intégrée même si j'avais spécifié que c'était le cas et même s'il avait cette propriété Build Action. J'ai perdu beaucoup de temps à me cogner la tête. J'ai intégré un fichier de code csharp avec .txt ajouté à son nom (xxx.cs.txt). Pour une raison quelconque, les méthodes GetManifestResourceNames () et GetManifestResourceStream () ne verront pas un fichier avec .cs dans son nom.

Je l'ai renommé simplement xxx.txt et tout allait bien.

Bizarre.

Belmiris
la source
1
Cela a sapé trop de temps aujourd'hui! Il l'intègrera si vous l'utilisez Resourcemais pas Embedded Resource, ce qui le rend encore plus étrange ... Le retrait .cs.du nom le fait fonctionner. Argh.
Matt
Le problème n'est pas que .cs. Le chemin / segment dans les fichiers est reconnu comme un fichier C # mais plutôt comme un CultureInfo.
frontlinebg
2
Il semble qu'avec une double extension cela ne fonctionne pas du tout.
Darion Badlydone
3

J'ai eu le même problème, grâce à Jay, j'ai trouvé que c'était des traits d'union dans le nom du répertoire.

ProjectName.ResourceFolder.Sub-Directorydevient ProjectName.ResourceFolder.Sub_Directorylorsque vous référencez le flux de ressources.

Aaron
la source
2

Dans mon cas, le problème était que le code recherchant la ressource était dans un projet différent de celui de la ressource elle-même.

Vous ne pouvez accéder qu'aux ressources qui se trouvent dans le même projet que le code. Je pensais pouvoir mettre toutes mes ressources dans le projet de page Web, mais j'ai également besoin d'images dans le projet de messagerie.

J'espère que cela aide quelqu'un dans la même situation que moi.

Je trouve l'appel vraiment utile Assembly.GetExecutingAssembly().GetManifestResourceNames();.

AxelWass
la source
Ce n'est pas vrai, du moins depuis 2011: à la fois Assembly.LoadFrom () et typeof, vous pouvez accéder aux ressources d'un autre projet
Marcelo Scofano
1

Au cas où cela aiderait quelqu'un d'autre, assurez-vous que la Assembly.GetExecutingAssembly()ligne est appelée à partir du même assembly qui a des ressources incorporées.

Krish
la source
Sauf si vous l'appelez à partir d'un autre projet, et dans ce cas, vous devez utiliser Assembly.LoadFrom () ou typeof pour pouvoir accéder aux ressources qui se trouvent dans un autre projet ...
Marcelo Scofano
1

Une solution simple et rationalisée consiste à avoir cette classe de 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();
        }
    }
}

Ensuite, lorsque vous ajoutez une ressource, vous créez une classe de lecteur C # dans le même dossier:

entrez la description de l'image ici

où la classe de lecture MyResource.cs est très simple:

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

Ainsi, chaque ressource aura une classe "shadow" qui sait la lire correctement.

Voici comment vous lisez la ressource dans votre code:

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

Et comme d'autres réponses le suggèrent, n'oubliez pas de définir "Ressource intégrée" dans la propriété Action de construction du fichier de ressources.

L'avantage de cette solution uniforme est

  1. moins de soucis pour trouver le nom complet correct de la ressource, en particulier lorsqu'elle est placée dans des dossiers imbriqués
  2. dans le cas où le dossier est renommé OU l'espace de noms par défaut dans les paramètres du projet est modifié, le code ne sera PAS cassé
VeganHunter
la source
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
la source
0

Vous devez décharger votre solution, puis éditer le projet, après avoir trouvé votre dossier et changer comme ceci:

<EmbeddedResource Include="yourpath" />
Tiga
la source
0

Bien que OP ait obtenu GetManifestResourceStream renvoyant NULL à partir des ressources du même assembly, certaines réponses suggéraient que lorsque les ressources se trouvaient dans un autre projet ou assembly, elles ne pouvaient pas être récupérées et étaient une cause équitable de GetManifestResourceStream retournant NULL.

Ce n'est pas vrai, du moins depuis 2011; comme je l'ai souligné dans certains commentaires ailleurs, Assembly.LoadFrom () ou typeof font l'affaire et par conséquent, vous pouvez accéder aux ressources qui se trouvent dans un autre projet.

J'ai ici un exemple modérément complexe pour illustrer; voici ma configuration de test:

entrez la description de l'image ici

Chemin vers un autre projet:

entrez la description de l'image ici

Capturé ici:

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

Et sur Form1.cs de WinFormFramework, je spécifie avec

Namespace.Folder.Resource

comme ça:

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

Et le résultat affiché dans la zone de texte: entrez la description de l'image ici

J'ai passé plusieurs heures à bien faire les choses; pour cela, j'ai dû utiliser beaucoup ceux-ci chez Immediate Window:

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

J'espère que ça aide quelqu'un

Marcelo Scofano
la source
-2

Vous devez probablement spécifier le chemin de votre fichier txt dans le GetManifestResourceStreamparamètre, ou vous pouvez essayer de coller le fichier txt dans le même répertoire que votre exécutable. J'espère que cela pourra aider!

Miles Watson
la source