以编程方式加载嵌入式资源文件

Posted

技术标签:

【中文标题】以编程方式加载嵌入式资源文件【英文标题】:Programmatically Load Embedded Resource File 【发布时间】:2015-08-12 16:31:09 【问题描述】:

我最近实现了以下代码,以便以编程方式构建项目/exe。在这个 exe 版本中,我想在资源中存储一堆“实际文件”,作为流。

这是我如何将文件添加到资源文件(单数)中,然后将该资源文件嵌入到编译器参数中:

        List<string> UserFiles = new List<string>();
        UserFiles.AddRange(Helpers.GetFilesInFolder(this.txt_Publish_Folder.Text));

        string folder = this.txt_Package_Location.Text;
        folder = folder + "\\Package_" + DateTime.Now.ToLongTimeString().Replace(":", "_").Replace(".", "_");
        Directory.CreateDirectory(folder);

        CompilerParameters parameters = new CompilerParameters();
        parameters.GenerateExecutable = true;
        parameters.IncludeDebugInformation = true;
        parameters.GenerateInMemory = false;
        parameters.WarningLevel = 3;
        parameters.CompilerOptions = "/optimize";
        parameters.OutputAssembly = folder + "\\Install_" + this.txt_AppName.Text + ".exe";
        parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
        parameters.ReferencedAssemblies.Add("System.dll");
        parameters.ReferencedAssemblies.Add("System.Core.dll");
        parameters.ReferencedAssemblies.Add("System.Data.dll");
        parameters.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
        parameters.ReferencedAssemblies.Add("System.Xml.dll");
        parameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");

        CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
        if (codeProvider.Supports(GeneratorSupport.Resources))
        
            string temp = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
            //create temp file first, because we want to append to it so as to have a single resource file with multiple stream entries...
            File.WriteAllText(temp, null);
            for (int i = 0; i < UserFiles.Count; i++ )
            
                byte[] FileBytes = File.ReadAllBytes(UserFiles[i]);

                using (FileStream stream = new FileStream(temp, FileMode.Append))
                
                    using (ResourceWriter writer = new ResourceWriter(stream))
                    
                        writer.AddResource(Path.GetFileName(UserFiles[i]), FileBytes);
                    
                
            
            parameters.EmbeddedResources.Add(temp);
        

        CompilerResults res = codeProvider.CompileAssemblyFromFile(parameters, @"C:\HIDDENPATH\Program.cs");

这很好用,不会产生任何错误,而且我实际上可以通过下面的代码在我构建的控制台应用程序(上面引用为 Prorgam.cs 的那个)中获取嵌入式资源文件。然而,我遇到的问题是将这个资源文件“加载”到应用程序程序集中/以某种方式获取它的值......这是我到目前为止要做的代码:

    static void Main(string[] args)
    
        Console.WriteLine("Checking for resources... please wait...");

        Assembly thisExe;
        thisExe = Assembly.GetExecutingAssembly();
        List<string> resources = thisExe.GetManifestResourceNames().ToList();

        if(resources.Count >= 1)
        
            try
            
                string baseName = resources[0];
                ResourceManager mgr = new ResourceManager(baseName, thisExe);
                Console.WriteLine("retrieved manager...");
                Console.ReadLine();
                ResourceSet set = mgr.GetResourceSet(Thread.CurrentThread.CurrentCulture, true, true);
                int count = set.Cast<object>().Count();
                Console.WriteLine("Found [" + count.ToString() + "] embedded resources. Would you like to enumerate them?");
                ConsoleKeyInfo input = Console.ReadKey();
                if (input.Key == ConsoleKey.Y)
                
                    // Build the string of resources.
                    foreach (string resource in resources)
                        Console.WriteLine(resource);
                

            
            catch (Exception ex)
            
                Console.Write(ex.Message);
                Console.ReadLine();
            
        

        Console.ReadLine();
    

每当我运行构建的 exe 时,我都会得到以下结果:

正在检查资源...请稍候... 找回经理... 找不到适合指定区域性或中性区域性的任何资源。确保“tmpCC59.tmp.resources”在编译时正确嵌入或链接到程序集“Install_testing”中,或者所有所需的附属程序集都是可加载的并且完全签名。

有人能告诉我为什么会这样吗?我已经尝试了我能想到的所有不同的文化集,但仍然没有结果。问题与我如何“嵌入”或我如何“加载”有关?

【问题讨论】:

【参考方案1】:

好的,伙计们,这就是我最终得到的结果,它实际上解决了我所有的问题。

    我现在不是尝试将“资源条目”添加到单个“资源文件”中,而是直接从源文件添加每个文件作为自己的资源(不知道为什么我想制作一个临时副本原始和使用流是必要的,但不是)像这样:

            for (int i = 0; i < WebAppFiles.Count; i++)
            
                parameters.EmbeddedResources.Add(WebAppFiles[i]);
            
    

    然后,当需要提取实际文件时,我不是尝试从单个“资源文件”加载“集合”,而是简单地从每个嵌入资源中提取数据,如下所示:

                    for (int i = 0; i < resources.Count; i++)
                    
                        Console.WriteLine("Extracting file: " + resources[i] + "...");
                        Stream stream = thisExe.GetManifestResourceStream(resources[i]);
                        byte[] bytes = new byte[(int)stream.Length];
                        stream.Read(bytes, 0, bytes.Length);
                        File.WriteAllBytes(dir + resources[i], bytes);
                    
    

这种新方式基本上意味着您告诉编译器“这些文件是 exe 所必需的并且是嵌入的”,因此当您调用“build”命令时,它实际上会为您完成所有工作并读取文件作为字节数组并将它们嵌入到可执行文件中。

然后,另一方面,您只需告诉程序您要将嵌入的资源保存为文件,使用它们自己的文件名。

希望这可以帮助其他尝试这样做的人...因为此类问题的所有其他帖子都有一些过于复杂的答案,这些答案要么不完整(例如:如何嵌入,但不检索)或没有实际工作。

【讨论】:

以上是关于以编程方式加载嵌入式资源文件的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式将资源加载为字节数组

以编程方式应用多资源 Kubernetes YAML 文件中的单个资源

以编程方式添加 ODR 资源

使用 AppDomain 将 DLL 作为嵌入式资源文件加载

如何以编程方式在android资源文件中设置项目windowBackground

以编程方式创建 pro 时如何在 Visual Studio 中查看资源文件内容