【问题标题】:How to extract from exe file the same .ico file as the one used to create this exe?如何从 exe 文件中提取与用于创建此 exe 的文件相同的 .ico 文件?
【发布时间】:2016-11-06 05:03:42
【问题描述】:

我正在尝试制作某种 SFX:让一个程序在另一个 Wrapping.exe 周围生成一个 wrapping.exe。

Wrapping.exe 将 Wrapping.exe 作为资源嵌入,执行时将 Wrapping.exe 保存到临时文件夹中,使用特定的命令行参数执行,然后将其删除。 Wrapped.exe 并不总是一个 .Net 程序,我没有它的源代码。

wrapping.exe 应在 .Net 3.5 中完成,以便在 Windows 7 SP1 及更高版本上使用,而无需事先安装任何 .Net。

Wrapping.exe 是使用 .Net 4.6 中的 Roselyn 使用 C# 程序生成的。

我需要 wrapping.exe 像资源管理器中的 Wrapping.exe 一样可视化。我已经使用硬编码的 .ico 文件进行了成功的测试。代码如下所示(简化):

var compilation = CSharpCompilation.Create(...);
var resourceDescription = new ResourceDescription( resourceName: "SFX.resourceName",
                                                   dataProvider: () => File.OpenRead("wrapped.exe"),
                                                   isPublic:     false);

using (var iconStream = File.OpenRead(@"wrapped.ico"))
using (var peStream = File.Create("wrapping.exe"))
using (var pdbStream = File.Create("wrapping.pdb"))
using (var win32resStream = compilation.CreateDefaultWin32Resources(
                                                              versionResource:  true,
                                                              noManifest:       false,
                                                              manifestContents: null,
                                                              iconInIcoFormat:  iconStream))
{
    var emitResult = compilation.Emit( peStream:          peStream,
                                       pdbStream:         pdbStream,
                                       manifestResources: new[] { resourceDescription },
                                       win32Resources:    win32resStream,
                                       options:           new EmitOptions(subsystemVersion: SubsystemVersion.Windows7));
  return emitResult;
}

现在我尝试从“wrapped.exe”获取 iconStream。我试图替换:

using (var iconStream = File.OpenRead(@"wrapped.ico"))

与:

var iconStream = new MemoryStream();
Icon icon = Icon.ExtractAssociatedIcon("wrapped.exe");
icon.Save(iconStream);
iconStream.Seek(0, SeekOrigin.Begin);

但我只得到一个 32*32 的图标。

如何提取完全相同的 .ico 文件(包括所有格式,例如 16*16 32 位 BMP、32*32 32 位 BMP、48*48 32 位 BMP、64*64 32 位 BMP 和 256* 256 32位PNG)作为用于创建'wrapped.exe'的那个?

【问题讨论】:

  • 这是错误的做法。该图标由程序集链接器作为 unmanaged 资源嵌入到可执行文件中。必需,因为操作系统不知道有关托管资源的 bean。所有本机 winapi 便利功能仅从资源中生成单个图标图像,从文件中挖掘完整图标需要痛苦的 pinvoke slog。将图标的托管版本与 Assembly.GetManifestResourceStream() 一起使用。
  • @HansPassant 我试图从非托管资源中提取图标,因为 Wrapped.exe 不一定是用 .Net 制作的(我的第一个用途是嵌入 InnoSetup exe)。
  • @HansPassant 进行了编辑以澄清它。
  • 我的需求与stackoverflow.com/questions/37261353/…密切相关。我会尝试使用IconLib

标签: c# .net windows .net-3.5


【解决方案1】:

也有做这项工作的网站 https://extracico.com/

【讨论】:

    【解决方案2】:

    这很容易使用IconLib。回复已经在this question:谢谢@Plutonix

    使用以下辅助函数(当然提取图标文件名不会被硬编码):

    static Stream GetIconStream_ExtractIconUsingIconLib(string fileToExecute)
    {
        var multiIcon = new MultiIcon();
        multiIcon.Load(fileToExecute);
    
        var extractedicoFileName = @"c:\temp\icon.ico";
        multiIcon.Save(extractedicoFileName, MultiIconFormat.ICO);
    
        return File.OpenRead(extractedicoFileName);
    }
    

    我们只需要替换:

    File.OpenRead(@"wrapped.ico")
    

    GetIconStream_ExtractIconUsingIconLib("wrapped.exe")
    

    这为我们提供了完整的解决方案:

    var compilation = CSharpCompilation.Create(...);
    var resourceDescription = new ResourceDescription( resourceName: "SFX.resourceName",
                                                       dataProvider: () => File.OpenRead("wrapped.exe"),
                                                       isPublic:     false);
    
    using (var iconStream = GetIconStream_ExtractIconUsingIconLib("wrapped.exe"))
    using (var peStream = File.Create("wrapping.exe"))
    using (var pdbStream = File.Create("wrapping.pdb"))
    using (var win32resStream = compilation.CreateDefaultWin32Resources(
                                                                  versionResource:  true,
                                                                  noManifest:       false,
                                                                  manifestContents: null,
                                                                  iconInIcoFormat:  iconStream))
    {
        var emitResult = compilation.Emit( peStream:          peStream,
                                           pdbStream:         pdbStream,
                                           manifestResources: new[] { resourceDescription },
                                           win32Resources:    win32resStream,
                                           options:           new EmitOptions(subsystemVersion: SubsystemVersion.Windows7));
      return emitResult;
    }
    

    【讨论】:

    • 注意:IconLib 仅适用于 32 位进程,但我能够在 .Net 4.61 进程中使用它(该库是为 .Net 2.0 制作的)。
    • 答案指出它是一个 NET 2.0 程序集,用户可能希望重新编译/更新它。我不知道这是否会允许它作为Any CPU 工作(不是该问题的一部分;))
    • @Plutonix 一定是 Win32.EnumResourceNames 的问题。在 64 位进程中,回调函数崩溃,但我没有时间调查,我可以处理 32 位进程。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多