GetManifestResourceStream возвращает NULL

105

Это приложение C # .NET 4.0:

Я встраиваю текстовый файл в качестве ресурса, а затем пытаюсь отобразить его в диалоговом окне:

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

Решение - MyProjSolution, а исполняемый файл - MyProj.exe. Help.txt - это встроенный ресурс. Однако поток равен нулю. Я пробовал MyProjSolution.Help.txt и MyProjSolution.MyProj.Help.txt, но ничего не работает.

Рон
источник
1
Используйте ildasm.exe, чтобы просмотреть имена .mresource в манифесте сборки. Избегайте попадания в эту яму невзгод, используйте вместо этого Project + Properties, вкладку Resource. Таким образом, вы можете просто использовать Properties.Resources.Help в своем исходном коде.
Ханс Пассан

Ответы:

189

Вы можете проверить правильность внедрения ресурсов, используя

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

при отладке. В нем будут перечислены все (полные имена) всех ресурсов, встроенных в сборку, в которой написан ваш код.

См. Assembly.GetManifestResourceNames () в MSDN.

Просто скопируйте соответствующее имя и используйте его вместо того, что вы определили в переменной resourceName.

Примечания - имя ресурса чувствительно к регистру, и если вы неправильно встроили файл ресурсов, он не будет отображаться в списке, возвращаемом вызовом GetManifestResourceNames (). Кроме того, убедитесь, что вы читаете ресурс из правильной сборки (если используется несколько сборок) - слишком легко получить ресурсы из текущей выполняющейся сборки, а не из сборки, на которую указывает ссылка.

РЕДАКТИРОВАТЬ - .NET Core. Подробную информацию о встраивании с использованием .NET Core
см. В этой публикации SO .

Получение информации о манифесте выглядит аналогичным - просто используйте, this.GetType().GetTypeInfo().Assembly.GetManifestResourceNames()чтобы получить манифест из сборки, в которой выполняется код.

Я еще не понял, как сделать эквивалент Assembly.GetExecutingAssembly()в .NET Core! если кто знает - дайте мне знать, и я обновлю этот ответ.

Джей
источник
5
Это сработало. ProjectName.Resources.Help.txt - это имя встроенного ресурса.
Рон
2
Рад, что помог! Если вы считаете, что этот пост дал ответ на ваш вопрос, не забудьте отметить его как принятый.
Джей
1
Да, в .Net Standard это должно быть [ProjectName]. [Namespace]. [Resource]. Мне не хватает названия проекта. Спасибо!
Билли Джейк О'Коннор
Моя проблема заключалась в использовании GetExecutingAssembly () вместо GetEntryAssembly (). Ресурс, который мне нужен, находился в исполняемом файле, но функция загрузки ресурса находилась в другом проекте, на который имеется ссылка, в том же решении.
lettucemode
63

У меня была аналогичная проблема, сначала проверьте, включен ли файл в ваш проект, затем перейдите в свойства и установите действие сборки этого файла на Embedded Resource. это сработало для меня.

Джитеш Чандра
источник
Это мне помогло! У меня были добавленные ранее файлы, которые по умолчанию были встроенными ресурсами, а затем те, которые я добавил позже, для которых было установлено значение «Нет». Visual Studio временами так раздражает. Хуже всего то, что все XML-файлы для ресурсов НЕ имеют настройки для действия сборки. Там должно быть установлено действие сборки.
Джон Сьют,
1
Не забудьте использовать пространство имен по умолчанию и путь к ресурсу. напр DefatultNameSpace.Infrastructure.Help.txt. Пространство имен по умолчанию на странице свойств вашего проекта и Infrastructureбудет папкой, в которой находится ваш файл
jabu.hlong
Это то, чего я хотел. Ура!
полосатая
20

Свойство встроенного файла «Build Action» должно быть установлено как «Embedded Resource» для правильного выполнения строки, которая приведена ниже:

Stream stream = assembly.GetManifestResourceStream(resourceName)

Щелкните правой кнопкой мыши файл, выберите свойство и затем установите для свойства «Действие сборки» значение «Встроенный ресурс»:

введите описание изображения здесь

Озлу
источник
Именно в чем была моя проблема. Спасибо!
Riki
1
Это была моя проблема. Я добавил его с помощью Resources.resx, и это не сработало.
Элдер Лима,
11

Вот причина моего нулевого значения.

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

GetManifestResourceStreamМетод всегда возвращается , NULLесли ресурс «Built действия» свойство не установлен на «внедренный ресурс»

После установки этого свойства со всеми необходимыми файлами assembly.GetManifestResourceStreamначинает возвращать правильный поток вместо NULL.

Нейт
источник
1
Еще раз спасибо. Это устранило мою проблему месяц или два назад, затем я забыл об этом, и у меня была такая же проблема, и она снова исправлена.
Rich
8

Просто предупреждение.

Я не мог получить доступ к своему файлу как к встроенному ресурсу, хотя я указал, что это так, и даже если у него было это свойство Build Action. Потратил много времени на то, чтобы биться головой. Я встроил файл кода csharp с добавленным к его имени .txt (xxx.cs.txt). По какой-то причине методы GetManifestResourceNames () и GetManifestResourceStream () не увидят файл с .cs в его имени.

Я переименовал его просто в xxx.txt, и все стало нормально.

Странно.

Бельмирис
источник
1
Сегодня на это ушло слишком много времени! Он вставит его, если вы его используете, Resourceно нет Embedded Resource, что делает его еще более странным ... Удаление .cs.из имени заставляет его работать. Ага.
Мэтт
Проблема не в том, что .cs. путь / сегмент в файлах распознается как файл C #, а не как CultureInfo.
frontlinebg
2
Вроде с двойным расширением вообще не работает.
Дарион Бэдлидон
3

У меня была такая же проблема, благодаря Джею я обнаружил, что в имени каталога есть дефис.

ProjectName.ResourceFolder.Sub-Directoryстановится, ProjectName.ResourceFolder.Sub_Directoryкогда вы ссылаетесь на поток ресурсов.

Аарон
источник
2

В моем случае проблема заключалась в том, что код, ищущий ресурс, находился в другом проекте, чем сам ресурс.

Вы можете получить доступ только к ресурсам, которые находятся в том же проекте, что и код. Я думал, что могу поместить все свои ресурсы в проект веб-страницы, но мне также нужны изображения в почтовом проекте.

Надеюсь, это поможет кому-то в той же ситуации, что и я.

Я считаю действительно полезным призвание Assembly.GetExecutingAssembly().GetManifestResourceNames();.

AxelWass
источник
Это неверно, по крайней мере, с 2011 года: и через Assembly.LoadFrom (), и через typeof вы можете получить доступ к ресурсам, которые находятся в другом проекте
Марсело Скофано,
1

Если это помогает кому-то еще, убедитесь, что Assembly.GetExecutingAssembly()линия вызывается из той же сборки, в которой есть встроенные ресурсы.

Криш
источник
За исключением случаев, когда вы вызываете его из другого проекта, и в этом случае вы должны использовать Assembly.LoadFrom () или typeof, чтобы вы могли получить доступ к ресурсам, которые находятся в другом проекте ...
Марсело Скофано,
1

Простое и оптимизированное решение - иметь этот базовый класс:

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

Затем, когда вы добавляете ресурс, вы создаете класс C # для чтения в той же папке:

введите описание изображения здесь

где класс чтения MyResource.cs очень прост:

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

Итак, у каждого ресурса будет «теневой» класс, который умеет его правильно читать.

Вот как вы читаете ресурс в своем коде:

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

И, как предлагали другие ответы, не забудьте установить «Встроенный ресурс» в свойстве Build Action файла ресурсов.

Преимущество этого унифицированного решения:

  1. меньше хлопот с поиском правильного полного имени ресурса, особенно при размещении во вложенных папках
  2. в случае, если папка переименована ИЛИ изменено пространство имен по умолчанию в настройках проекта, код НЕ сломается
VeganHunter
источник
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

Вам нужно выгрузить свое решение, затем отредактировать проект, а затем найти свою папку и изменить так:

<EmbeddedResource Include="yourpath" />
Тига
источник
0

Хотя OP получил GetManifestResourceStream, возвращающий NULL из ресурсов в той же сборке, в некоторых ответах предполагалось, что, когда ресурсы находятся в другом проекте или сборке, они не могут быть получены, и являются справедливой причиной того, что GetManifestResourceStream возвращает NULL.

Это неправда, по крайней мере, с 2011 года; как я указывал в некоторых комментариях в другом месте, Assembly.LoadFrom () или typeof делают трюк, и в результате вы можете получить доступ к ресурсам, которые находятся в другом проекте.

Для иллюстрации у меня есть умеренно сложный пример; это моя тестовая установка:

введите описание изображения здесь

Путь к другому проекту:

введите описание изображения здесь

Захвачено здесь:

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

А на Form1.cs из WinFormFramework я указываю с помощью

Пространство имен.Folder.Resource

как это:

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

И результат отображается в текстовом поле: введите описание изображения здесь

Я потратил несколько часов, чтобы понять это правильно; для этого мне пришлось использовать много их в Immediate Window:

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

Надеюсь, это кому-то поможет

Марсело Скофано
источник
-2

Вероятно, вам нужно указать путь к вашему txt-файлу в GetManifestResourceStreamпараметре, или вы можете попробовать вставить txt-файл в тот же каталог, что и ваш исполняемый файл. Надеюсь, это поможет!

Майлз Уотсон
источник