简体   繁体   English

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

[英]Programmatically Load Embedded Resource File

i recently implemented the following code in order to programatically build a project/exe. 我最近实现了以下代码,以便以编程方式构建项目/ exe。 In this exe build, i wanted to store a bunch of "actual files" inside of resources, as streams. 在此exe版本中,我想将一堆“实际文件”作为流存储在资源内部。

Here's how i'm adding the files into the resource file (singular), and then embedding that resource file into the compiler parameters: 这是我将文件添加到资源文件(单数),然后将该资源文件嵌入到编译器参数中的方式:

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

This works great, doesn't yield any errors, and i can actually get the embedded resource file out in the Console Application i built (the one referenced above as Prorgam.cs) through the code below. 这很好用,不会产生任何错误,实际上我可以通过以下代码在我构建的控制台应用程序(上面称为Prorgam.cs)中获取嵌入式资源文件。 The problem i am having however is with "Loading" this resource file into the application assembly / getting its values out somehow... here's the code i have so far to do that: 但是,我遇到的问题是将这个资源文件“加载”到应用程序程序集中/以某种方式获取其值...这是我到目前为止要做的代码:

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

Whenever i run the built exe, i get the following result: 每当我运行内置的exe时,都会得到以下结果:

Checking for resources... please wait...
retrieved manager...

Could not find any resources appropriate for the specified culture or the neutral culture.  Make sure "tmpCC59.tmp.resources" was correctly embedded or linked into assembly "Install_testing" at compile time, or that all the satellite assemblies required are loadable and fully signed.

Can someone please tell me why this is happenning? 有人可以告诉我为什么会这样吗? i've tried all different culture sets i could think of and still nothing comes out. 我尝试了所有我能想到的不同文化背景,但仍然没有结果。 Is the problem related to how i'm "Embedding" or how i'm "Loading" ? 问题与我如何“嵌入”或如何“加载”有关吗?

Ok folks, so this is what i ended up with, which actually fixed all my problems. 好的,伙计们,这就是我最终得到的结果,它实际上解决了我所有的问题。

  1. Instead of trying to add "Resource Entries" into a single "Resource File", i'm now adding every file as its own resource, directly from the source file (don't know why i thought making a temp copy of the original and using a stream was necessary but it's not) like this: 现在,我不是尝试将“资源条目”添加到单个“资源文件”中,而是直接从源文件中将每个文件作为其自己的资源添加(不知道为什么我想对原始文件和文件进行临时复制)使用流是必要的,但不是这样的:

      for (int i = 0; i < WebAppFiles.Count; i++) { parameters.EmbeddedResources.Add(WebAppFiles[i]); } 
  2. Then, when comes time to extract the actual files, instead of trying to load a "set" from a single "resource file", i simply extract the data from each embedded resource, like this: 然后,是时候提取实际文件了,而不是尝试从单个“资源文件”中加载“集合”,我只是从每个嵌入式资源中提取数据,如下所示:

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

This new way basically means that you tell the compiler that "these files are necessary for the exe and are embedded", so when you call the "build" command, it actually does all the work for you and reads the files in as byte arrays and embeds those into the executable file. 这种新方法基本上意味着您告诉编译器“这些文件对于exe是必需的并且是嵌入式的”,因此,当您调用“ build”命令时,它实际上为您完成了所有工作并以字节数组的形式读取文件并将它们嵌入可执行文件中。

Then, at the other hand, you simply tell the program that you want to save the embedded resources as files, using their own file names. 然后,另一方面,您只需使用自己的文件名告诉程序您想要将嵌入式资源另存为文件。

Hopefully this helps someone else who is trying to do this... as every other post for this type of question had some over complicated answers that were either incomplete (eg: how to embed, but not retrieve) or didn't actually work. 希望这可以帮助其他尝试执行此操作的人...因为此类问题的每篇其他帖子都有一些过于复杂的答案,这些答案要么不完整(例如:如何嵌入但不检索),要么实际上不起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM