GetManifestResourceStream zwraca NULL

105

To jest aplikacja C # .NET 4.0:

Osadzam plik tekstowy jako zasób, a następnie próbuję wyświetlić go w oknie dialogowym:

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

Rozwiązaniem jest MyProjSolution, a plikiem wykonywalnym jest MyProj.exe. Help.txt to zasób osadzony. Jednak strumień jest pusty. Próbowałem MyProjSolution.Help.txt i MyProjSolution.MyProj.Help.txt, ale wydaje się, że nic nie działa.

Ron
źródło
1
Użyj ildasm. Exe, aby przejrzeć nazwy zasobów w manifeście zestawu. Unikaj wpadania w tę otchłań nieszczęść, zamiast tego użyj opcji Projekt + Właściwości, zakładka Zasoby. Możesz więc po prostu użyć Properties.Resources.Help w swoim kodzie źródłowym.
Hans Passant

Odpowiedzi:

189

Możesz sprawdzić, czy zasoby są poprawnie osadzone przy użyciu

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

podczas debugowania. Spowoduje to wyświetlenie wszystkich (w pełni kwalifikowanych nazw) wszystkich zasobów osadzonych w zestawie, w którym jest zapisany kod.

Zobacz Assembly.GetManifestResourceNames () w witrynie MSDN.

Po prostu skopiuj odpowiednią nazwę i użyj jej zamiast tego, co zdefiniowałeś w zmiennej „resourceName”.

Uwagi - w nazwie zasobu rozróżniana jest wielkość liter i jeśli niepoprawnie osadziłeś plik zasobu, nie pojawi się on na liście zwróconej przez wywołanie GetManifestResourceNames (). Ponadto - upewnij się, że czytasz zasób z prawidłowego zestawu (jeśli używanych jest wiele zestawów) - zbyt łatwo jest pobrać zasoby z aktualnie wykonywanego zestawu, a nie z zestawu, do którego się odwołuje.

EDYCJA - .NET Core
Zobacz ten wpis SO, aby uzyskać szczegółowe informacje na temat osadzania przy użyciu platformy .NET Core.

Pobieranie informacji manifestu wygląda podobnie - po prostu użyj go, this.GetType().GetTypeInfo().Assembly.GetManifestResourceNames()aby pobrać manifest z zestawu, w którym wykonywany jest kod.

Jeszcze nie wymyśliłem, jak zrobić odpowiednik Assembly.GetExecutingAssembly()w .NET Core! jeśli ktoś wie - daj mi znać, a zaktualizuję tę odpowiedź.

Sójka
źródło
5
To się udało. ProjectName.Resources.Help.txt była nazwą osadzonego zasobu.
Ron,
2
Cieszę się, że pomogłem! Jeśli uważasz, że ten post odpowiedział na Twoje pytanie, nie zapomnij zaznaczyć tego jako zaakceptowanej odpowiedzi.
Jay
1
Tak, więc w .Net Standard powinno to być [ProjectName]. [Namespace]. [Resource]. Brakuje nazwy projektu. Dzięki!
Billy Jake O'Connor,
Mój problem polegał na użyciu GetExecutingAssembly () zamiast GetEntryAssembly (). Zasób, który chciałem, znajdował się w pliku wykonywalnym, ale funkcja ładowania zasobu znajdowała się w innym, przywoływanym projekcie w tym samym rozwiązaniu.
lettucemode
63

Miałem podobny problem, najpierw sprawdź, czy plik jest zawarty w twoim projekcie, a następnie przejdź do właściwości i ustaw akcję kompilacji tego pliku na Zasób osadzony. to działało dla mnie.

Jithesh Chandra
źródło
To mi pomogło! Niektóre pliki zostały dodane wcześniej, które domyślnie były zasobami osadzonymi, a następnie te dodane później, które były ustawione na „Brak”. Visual Studio jest czasami irytujące. Najgorsze jest to, że wszystkie pliki XML zasobów NIE mają ustawienia dla akcji kompilacji. Powinien mieć tam ustawioną akcję kompilacji.
John Suit
1
Pamiętaj, aby użyć domyślnej przestrzeni nazw i ścieżki do zasobu. np DefatultNameSpace.Infrastructure.Help.txt. Domyślna przestrzeń nazw na stronie właściwości projektu i Infrastructurebędzie folder, który plik jest w
jabu.hlong
Właśnie tego chciałem Pozdrawiam!
pręgowany
20

Właściwość „Build Action” osadzonego pliku powinna być ustawiona na „Embedded Resource”, aby poprawnie uruchomić wiersz podany poniżej:

Stream stream = assembly.GetManifestResourceStream(resourceName)

Kliknij plik prawym przyciskiem myszy, kliknij właściwość, a następnie ustaw właściwość „Build Action” na „Embedded Resource”:

wprowadź opis obrazu tutaj

Ozlu
źródło
Dokładnie jaki był mój problem. Dzięki!
Riki
1
To był mój problem. Dodałem go za pomocą Resources.resx i nie działał.
Hélder Lima
11

Oto przyczyna mojej wartości zerowej.

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

GetManifestResourceStreamMetoda będzie zawsze powraca NULLjeśli właściwość zasobu „zbudowany działania” nie jest ustawiony na „zasobu osadzonego”

Po ustawieniu tej właściwości ze wszystkimi potrzebnymi plikami assembly.GetManifestResourceStreamzaczyna zwracać poprawny strumień zamiast NULL.

Nate
źródło
1
Dzięki jeszcze raz. To rozwiązało mój problem miesiąc lub dwa temu, a potem zapomniałem o nim i miałem ten sam problem i naprawiłem go ponownie.
Rich
8

Tylko ostrzeżenie.

Nie mogłem uzyskać dostępu do mojego pliku jako zasobu osadzonego, mimo że określiłem, że był i mimo że miał tę właściwość Build Action. Zmarnowałem dużo czasu na walenie głową. Osadziłem plik kodu csharp z .txt dołączonym do jego nazwy (xxx.cs.txt). Z jakiegoś powodu metody GetManifestResourceNames () i GetManifestResourceStream () nie zobaczą pliku z .cs w nazwie.

Zmieniłem nazwę po prostu na xxx.txt i wszystko było w porządku.

Dziwne.

Belmiris
źródło
1
To zajęło dziś zbyt dużo czasu! Osadzi go, jeśli go użyjesz, Resourceale nie Embedded Resource, co czyni go jeszcze dziwniejszym ... Usunięcie .cs.z nazwy sprawia, że ​​działa. Argh.
Matt
Problem nie polega na tym, że .cs. ścieżka / segment w plikach jest rozpoznawana jako plik C #, ale raczej jako CultureInfo.
frontlinebg
2
Wygląda na to, że z podwójnym przedłużeniem w ogóle nie działa.
Darion Badlydone
3

Miałem ten sam problem, dzięki Jayowi stwierdziłem, że w nazwie katalogu były myślniki.

ProjectName.ResourceFolder.Sub-Directorystaje się, ProjectName.ResourceFolder.Sub_Directorygdy odwołujesz się do strumienia zasobów.

Aaron
źródło
2

W moim przypadku problem polegał na tym, że kod szukający zasobu znajdował się w innym projekcie niż sam zasób.

Możesz uzyskać dostęp tylko do zasobów, które są w tym samym projekcie, w którym znajduje się kod. Pomyślałem, że mogę umieścić wszystkie moje zasoby w projekcie strony internetowej, ale potrzebuję również obrazów w projekcie poczty.

Mam nadzieję, że pomoże to komuś w takiej samej sytuacji, w jakiej byłem.

Uważam, że powołanie jest naprawdę przydatne Assembly.GetExecutingAssembly().GetManifestResourceNames();.

AxelWass
źródło
To nieprawda, przynajmniej od 2011 roku: zarówno przez Assembly.LoadFrom (), jak i typeof można uzyskać dostęp do zasobów, które są w innym projekcie
Marcelo Scofano
1

W przypadku, gdy pomaga to komukolwiek innemu, upewnij się, że Assembly.GetExecutingAssembly()linia jest wywoływana z tego samego zestawu, który ma osadzone zasoby.

Krish
źródło
Z wyjątkiem sytuacji, gdy wywołujesz go z innego projektu iw tym przypadku powinieneś użyć Assembly.LoadFrom () lub typeof, aby uzyskać dostęp do zasobów, które są w innym projekcie ...
Marcelo Scofano
1

Prostym i usprawnionym rozwiązaniem jest posiadanie tej klasy bazowej :

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

Następnie podczas dodawania zasobu tworzysz klasę czytnika C # w tym samym folderze:

wprowadź opis obrazu tutaj

gdzie klasa czytelnika MyResource.cs jest bardzo prosta:

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

Zatem każdy zasób będzie miał klasę „shadow”, która wie, jak go poprawnie odczytać.

Oto jak odczytujesz zasób w swoim kodzie:

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

I jak sugerowały inne odpowiedzi, nie zapomnij ustawić „Zasobu osadzonego” we właściwości Akcja kompilacji pliku zasobów.

Zaletą tego jednolitego rozwiązania jest

  1. mniej kłopotów ze znalezieniem poprawnej pełnej nazwy zasobu, zwłaszcza gdy jest umieszczony w zagnieżdżonych folderach
  2. w przypadku zmiany nazwy folderu LUB zmiany domyślnej przestrzeni nazw w ustawieniach projektu kod NIE ulegnie awarii
VeganHunter
źródło
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>
Maniak
źródło
0

Musisz zwolnić swoje rozwiązanie, a następnie edytować projekt, a następnie znaleźć folder i zmienić go w ten sposób:

<EmbeddedResource Include="yourpath" />
Tiga
źródło
0

Chociaż OP otrzymał GetManifestResourceStream zwracając NULL z zasobów w tym samym zestawie, niektóre odpowiedzi sugerowały, że gdy zasoby znajdują się w innym projekcie lub zestawie, nie można ich odzyskać i są uzasadnioną przyczyną zwracania wartości NULL przez GetManifestResourceStream.

To nieprawda, przynajmniej od 2011 roku; jak wskazałem w niektórych komentarzach w innym miejscu, Assembly.LoadFrom () lub typeof załatwiają sprawę iw rezultacie możesz uzyskać dostęp do zasobów, które są w innym projekcie.

Mam tutaj do zilustrowania umiarkowanie złożony przykład; to jest moja konfiguracja testowa:

wprowadź opis obrazu tutaj

Ścieżka do innego projektu:

wprowadź opis obrazu tutaj

Przechwycono tutaj:

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

A na Form1.cs z WinFormFramework określam za pomocą

Namespace.Folder.Resource

tak:

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

Wynik wyświetlany w polu tekstowym: wprowadź opis obrazu tutaj

Spędziłem kilka godzin, aby zrobić to dobrze; w tym celu musiałem często używać tych w Immediate Window:

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

Mam nadzieję, że to komuś pomoże

Marcelo Scofano
źródło
-2

Prawdopodobnie musisz podać ścieżkę do pliku txt w GetManifestResourceStreamparametrze lub możesz spróbować umieścić plik txt w tym samym katalogu, co plik wykonywalny. Mam nadzieję, że to pomoże!

Miles Watson
źródło