C# 如何获取exe的嵌入式资源的文件名?

C# 如何获取exe的嵌入式资源的文件名?,c#,C#,在VisualStudio中,我有一个构建操作设置为“嵌入式资源”的文件。我想在运行时提取此文件,但无法获取文件名: // returns null Assembly.GetExecutingAssembly().GetManifestResourceInfo(resourceName).FileName 请注意GetManifestResourceInfo(resourceName)。如果文件名与清单文件不同,则仅获取包含清单资源的文件名。关于清单,请参见此处: 可以使用assembly.

在VisualStudio中,我有一个构建操作设置为“嵌入式资源”的文件。我想在运行时提取此文件,但无法获取文件名:

// returns null
Assembly.GetExecutingAssembly().GetManifestResourceInfo(resourceName).FileName

请注意GetManifestResourceInfo(resourceName)。如果文件名与清单文件不同,则仅获取包含清单资源的文件名。关于清单,请参见此处:

可以使用assembly.GetManifestResourceNames方法获取程序集中所有资源的名称列表

比如:

//str包含所有嵌入的资源名称

 string[] str = assembly.GetManifestResourceNames();

请注意,清单资源名称确实包括原始文件名和项目相对子目录

另一个答案中缺少的关键信息(使该答案变得毫无用处)是,项目名称和包含原始文件的任何文件夹/目录都表示为资源名称中的组件。整个清单资源名称使用
”字符分隔到这些组件中。
字符

掌握了这些知识,您应该能够正确地处理资源。特别是,您需要删除名称的第一个组件(即项目名称),然后将名称的最后一个组件以外的所有组件都视为目录名称

这有点棘手,因为a)您的文件名可能有一个扩展名(因此其中已经有一个
字符),b)编译器将转义(以基本方式)文件夹/子目录名中的任何
字符

下面是一个显示基本方法的代码示例:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(string.Join(Environment.NewLine,
            Assembly.GetEntryAssembly().GetManifestResourceNames()));
        Assembly assembly = Assembly.GetEntryAssembly();
        string exeDirectory = Path.GetDirectoryName(assembly.Location);

        foreach (string resourceName in assembly.GetManifestResourceNames())
        {
            string fileName = _GetFileNameFromResourceName(resourceName),
                directory = Path.GetDirectoryName(fileName);

            if (!string.IsNullOrEmpty(directory))
            {
                Directory.CreateDirectory(directory);
            }

            using (Stream outputStream =
                File.OpenWrite(Path.Combine(exeDirectory, fileName)))
            {
                assembly.GetManifestResourceStream(resourceName).CopyTo(outputStream);
            }
        }
    }

    private static string _GetFileNameFromResourceName(string resourceName)
    {
        // NOTE: this code assumes that all of the file names have exactly one
        // extension separator (i.e. "dot"/"period" character). I.e. all file names
        // do have an extension, and no file name has more than one extension.
        // Directory names may have periods in them, as the compiler will escape these
        // by putting an underscore character after them (a single character
        // after any contiguous sequence of dots). IMPORTANT: the escaping
        // is not very sophisticated; do not create folder names with leading
        // underscores, otherwise the dot separating that folder from the previous
        // one will appear to be just an escaped dot!

        StringBuilder sb = new StringBuilder();
        bool escapeDot = false, haveExtension = false;

        for (int i = resourceName.Length - 1; i >= 0 ; i--)
        {
            if (resourceName[i] == '_')
            {
                escapeDot = true;
                continue;
            }

            if (resourceName[i] == '.')
            {
                if (!escapeDot)
                {
                    if (haveExtension)
                    {
                        sb.Append('\\');
                        continue;
                    }
                    haveExtension = true;
                }
            }
            else
            {
                escapeDot = false;
            }

            sb.Append(resourceName[i]);
        }

        string fileName = Path.GetDirectoryName(sb.ToString());

        fileName = new string(fileName.Reverse().ToArray());

        return fileName;
    }
}
使用上述代码创建一个新项目,然后将嵌入的资源添加到项目中,无论是否在项目文件夹中,以查看结果

注意:您可能会发现筛选资源名称列表很有用。例如,如果您的项目具有使用设计器添加的传统资源(即显示项目属性,然后单击“资源”选项卡并在其中添加资源,而不是显式地作为嵌入资源),则您将获得名为
MyProjectName.Resources.Resources
的资源。否则,您将获得每个清单资源


注意:请记住,要写入运行可执行文件的目录,您可能需要比当前进程更高级别的权限。例如,如果用户以管理员身份将您的EXE复制到“Program Files”目录中的某个目录,但随后尝试以受限用户身份运行该程序。正如上面的评论员Luaan在你的问题下暗示的那样,看起来你最好只是实现某种安装程序。(Visual Studio提供对基本Install Shield产品的免费访问,或者您可以使用WiX…实际上不需要从头编写安装程序来实现安装程序)。

一旦嵌入资源,就没有文件名。既然您的EXE可以在任何地方运行,包括在不同于编译它的机器上运行,那么文件名对您有什么好处?你到底想做什么?你想解决什么具体问题?我正在将我的应用程序依赖项打包到我的exe中。我避免安装程序,因为他们可能需要管理员权限,或者某些机器可能会阻止它。对不起,这并不能回答我提出的问题。为什么要从资源中获取原始文件名?如果您获得了此文件名,您将如何处理它?一旦获得了文件名,我将把资源流复制到它们相应的输出文件名,并基于Assembly.GetEntryAssembly().Location将它们与exe放在一起。此后,我的应用程序exe将能够使用DLL、配置。@happymeal噢,您想避免安装程序,因为存在阻止/权限问题。。。所以你写了一个安装程序。这非常有意义。它提供了嵌入资源的列表,但不包含文件名。支持所有资源文件名不应超过一个点的未经检查的协议比只保留一个以资源名为键、以文件名为键的字典更糟糕value@astef:也许你错过了答案中的陈述:“这是一个显示基本方法的代码示例”。提供说明性代码而不必为他们完成OP的所有工作是正常的。这很难让答案“没有用处”“。我猜您想要的是可以复制/粘贴到您自己的程序中的东西,并且只需要这些就可以了?问题是无法从资源名称字符串中获取真实的文件名。您编写的代码只适用于某些特殊文件名,对于大多数互联网搜索者,您的答案是有害的。@astef:“对于大多数互联网搜索者,您的答案是有害的”——真的吗?你真的相信,对于大多数寻求解决方案的人来说,他们的文件名会有多个扩展名,同时无法阅读有关此问题的评论警告并调整他们的方法以适应吗?这是一个惊人的庞大人口,在我看来,这是一个非常小的环境交叉点。只有当人们不解释限制或问题以及如何安全地避免它们时,这才是有害的。枪是有害的,但有了正确的知识,出现问题的机会会大大减少。