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 เพื่อดูชื่อ. แหล่งที่มาในรายการประกอบ หลีกเลี่ยงการตกอยู่ในหลุมแห่งความทุกข์ยากนี้ให้ใช้ Project + Properties แท็บทรัพยากรแทน ดังนั้นคุณสามารถใช้ Properties.Resources ช่วยในซอร์สโค้ดของคุณ
Hans Passant

คำตอบ:


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
โปรดดูโพสต์ SOนี้สำหรับรายละเอียดเกี่ยวกับวิธีการฝังโดยใช้. NET Core

การดึงข้อมูลรายการดูเหมือนจะคล้ายกัน - เพียงแค่ใช้this.GetType().GetTypeInfo().Assembly.GetManifestResourceNames()เพื่อรับรายการจากแอสเซมบลีที่โค้ดกำลังทำงาน

ฉันยังไม่ทราบวิธีการเทียบเท่า Assembly.GetExecutingAssembly()ใน. NET Core! ถ้าใครรู้ - โปรดแจ้งให้เราทราบแล้วเราจะอัปเดตคำตอบนี้


5
ที่ได้ผล ProjectName.Resources.Help.txt คือชื่อทรัพยากรที่ฝังไว้
รอน

2
ฉันดีใจที่ได้ช่วยเหลือ! หากคุณรู้สึกว่าโพสต์นี้ตอบคำถามของคุณได้โปรดอย่าลืมทำเครื่องหมายว่าเป็นคำตอบที่ยอมรับ
Jay

1
ใช่แล้วใน. Net Standard ควรเป็น [ProjectName] [Namespace] [แหล่งข้อมูล] ฉันไม่มีชื่อโครงการ ขอบคุณ!
Billy Jake O'Connor

ปัญหาของฉันคือใช้ GetExecutingAssembly () แทน GetEntryAssembly () ทรัพยากรที่ฉันต้องการอยู่ในไฟล์ปฏิบัติการ แต่ฟังก์ชันในการโหลดทรัพยากรอาศัยอยู่ในโปรเจ็กต์อื่นที่อ้างถึงในโซลูชันเดียวกัน
lettucemode

63

ฉันมีปัญหาที่คล้ายกันให้ตรวจสอบก่อนว่าไฟล์นั้นรวมอยู่ในโครงการของคุณจากนั้นไปที่คุณสมบัติและตั้งค่าการสร้างไฟล์นั้นเป็นทรัพยากรแบบฝัง สิ่งนี้ได้ผลสำหรับฉัน


สิ่งนี้ช่วยฉันได้! ฉันมีไฟล์บางไฟล์ที่เพิ่มไว้ก่อนหน้านี้ซึ่งเป็นค่าเริ่มต้นเป็นทรัพยากรที่ฝังตัวจากนั้นไฟล์ที่ฉันเพิ่มในภายหลังซึ่งถูกตั้งค่าเป็น "ไม่มี" Visual Studio น่ารำคาญมากในบางครั้ง ส่วนที่แย่ที่สุดคือไฟล์ XML ทั้งหมดสำหรับทรัพยากรไม่มีการตั้งค่าสำหรับการสร้างการดำเนินการ ควรมีชุดการดำเนินการสร้างที่นั่น
John Suit

1
อย่าลืมใช้เนมสเปซเริ่มต้นและเส้นทางไปยังทรัพยากร เช่นDefatultNameSpace.Infrastructure.Help.txt. เนมสเปซเริ่มต้นในหน้าคุณสมบัติโปรเจ็กต์ของคุณและInfrastructureจะเป็นโฟลเดอร์ที่คุณไฟล์อยู่
jabu.hlong

นี่คือสิ่งที่ฉันต้องการไชโย!
tabby

20

ควรตั้งค่าคุณสมบัติ"Build Action"ของไฟล์ที่ฝังไว้เป็น"Embedded Resource"เพื่อเรียกใช้บรรทัดที่ระบุไว้ด้านล่างอย่างถูกต้อง:

Stream stream = assembly.GetManifestResourceStream(resourceName)

คลิกขวาที่ไฟล์คลิกคุณสมบัติจากนั้นตั้งค่าคุณสมบัติ "Build Action" เป็น "Embedded Resource":

ใส่คำอธิบายภาพที่นี่


สิ่งที่เป็นปัญหาของฉัน ขอบคุณ!
Riki

1
นี่คือปัญหาของฉัน ฉันเพิ่มโดยใช้ Resources.resx และไม่ได้ผล
Hélder Lima

11

นี่คือสาเหตุของค่า null ของฉัน

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

GetManifestResourceStreamวิธีมักจะให้ผลตอบแทนNULLถ้าคุณสมบัติทรัพยากร 'สร้างการกระทำ' ไม่ได้ตั้ง 'ทรัพยากรฝัง'

หลังจากการตั้งค่าคุณสมบัตินี้กับไฟล์ทั้งหมดที่จำเป็นเริ่มกลับมากระแสที่ถูกต้องแทนassembly.GetManifestResourceStreamNULL


1
ขอบคุณอีกครั้ง. สิ่งนี้แก้ไขปัญหาของฉันเมื่อเดือนหรือสองเดือนที่แล้วจากนั้นฉันก็ลืมมันและมีปัญหาเดียวกันและได้รับการแก้ไขอีกครั้ง
รวย

8

แค่คำเตือน.

ฉันไม่สามารถเข้าถึงไฟล์ของฉันเป็นทรัพยากรในตัวแม้ว่าฉันจะระบุว่าเป็นและแม้ว่ามันจะมีคุณสมบัติ Build Action ก็ตาม เสียเวลาโขกหัวมาก ฉันฝังไฟล์รหัส csharp ด้วย. txt ต่อท้ายชื่อ (xxx.cs.txt) ด้วยเหตุผลบางประการเมธอด GetManifestResourceNames () และ GetManifestResourceStream () จะไม่เห็นไฟล์ที่มีชื่อ. cs

ฉันเปลี่ยนชื่อเป็น xxx.txt และทุกอย่างเรียบร้อยดี

แปลก.


1
วันนี้ประหยัดเวลามากเกินไป! มันจะฝังถ้าคุณใช้Resourceแต่ไม่ใช่Embedded Resourceซึ่งทำให้มันดูแปลกกว่า ... การลบออก.cs.จากชื่อทำให้มันใช้งานได้ อ๊าก.
Matt

ปัญหาไม่ได้อยู่ที่. cs เส้นทาง / เซ็กเมนต์ในไฟล์ได้รับการยอมรับว่าเป็นไฟล์ C # แต่เป็น CultureInfo
frontlinebg

2
ดูเหมือนว่าการขยายสองครั้งจะไม่ได้ผลเลย
Darion Badlydone

3

ฉันมีปัญหาเดียวกันขอบคุณ Jay ฉันพบว่ามันเป็นยัติภังค์ในชื่อไดเรกทอรี

ProjectName.ResourceFolder.Sub-Directoryกลายเป็นProjectName.ResourceFolder.Sub_Directoryเมื่อคุณอ้างอิงสตรีมทรัพยากร


2

ในกรณีของฉันปัญหาคือรหัสที่ค้นหาทรัพยากรอยู่ในโครงการอื่นที่ทรัพยากรนั้นเอง

คุณสามารถเข้าถึงทรัพยากรที่อยู่ในโครงการเดียวกันกับรหัสเท่านั้น ฉันคิดว่าฉันสามารถใส่ทรัพยากรทั้งหมดในโปรเจ็กต์หน้าเว็บได้ แต่ฉันก็ต้องการรูปภาพในโปรเจ็กต์อีเมลด้วย

หวังว่านี่จะช่วยใครบางคนในสถานการณ์เดียวกันกับฉัน

Assembly.GetExecutingAssembly().GetManifestResourceNames();ผมพบว่าการโทรที่มีประโยชน์จริงๆ


สิ่งนี้ไม่เป็นความจริงอย่างน้อยตั้งแต่ปี 2011: ผ่านทั้งAssembly.LoadFrom ()และtypeofคุณสามารถเข้าถึงทรัพยากรที่อยู่ในโครงการอื่น
Marcelo Scofano

1

ในกรณีที่ช่วยคนอื่นตรวจสอบให้แน่ใจว่าAssembly.GetExecutingAssembly()มีการเรียกบรรทัดจากแอสเซมบลีเดียวกันซึ่งมีทรัพยากรฝังอยู่


ยกเว้นว่าคุณเรียกจากโปรเจ็กต์อื่นและในกรณีนี้คุณควรใช้Assembly.LoadFrom ()หรือtypeofเพื่อให้คุณสามารถเข้าถึงทรัพยากรที่อยู่ในโปรเจ็กต์อื่นได้ ...
Marcelo Scofano

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

และตามคำตอบอื่น ๆ ที่แนะนำอย่าลืมตั้งค่า "Embedded Resource" ในคุณสมบัติ Build Action ของไฟล์ทรัพยากร

ข้อดีของโซลูชันชุดนี้คือ

  1. ไม่ยุ่งยากกับการค้นหาชื่อเต็มของทรัพยากรโดยเฉพาะอย่างยิ่งเมื่อวางไว้ในโฟลเดอร์ที่ซ้อนกัน
  2. ในกรณีที่เปลี่ยนชื่อโฟลเดอร์หรือเนมสเปซเริ่มต้นในการตั้งค่าโปรเจ็กต์โค้ดจะไม่แตก

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จากทรัพยากรใน Assembly เดียวกันคำตอบบางคำแนะนำว่าเมื่อทรัพยากรอยู่ใน Project หรือ Assembly อื่นจะไม่สามารถเรียกคืนได้และเป็นสาเหตุที่ยุติธรรมของ GetManifestResourceStream ที่คืนค่า NULL

นี่ไม่เป็นความจริงอย่างน้อยก็ตั้งแต่ปี 2554 ตามที่ฉันชี้ไปในความคิดเห็นที่อื่น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 ฉันระบุด้วย

เนมสเปซโฟลเดอร์ทรัพยากร

เช่นนั้น:

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 ไว้ในไดเร็กทอรีเดียวกับไฟล์ปฏิบัติการของคุณ หวังว่าจะช่วยได้!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.