Search code examples
c#.netwindows.net-3.5

How to extract from exe file the same .ico file as the one used to create this exe?


I'm trying to make some kind of SFX: make a program generating a wrapping.exe around another wrapped.exe.

Wrapping.exe embed wrapped.exe as resource and, when executed, wrapped.exe is saved into temporary folder, executed with specific command line arguments and then deleted. wrapped.exe is not always a .Net program and I don't have source code for it.

wrapping.exe should be done in .Net 3.5 to be usable on Windows 7 SP1 and upper without any prior .Net installation.

Wrapping.exe is generated with a C# program using Roselyn in .Net 4.6.

I need wrapping.exe to be visualized like wrapped.exe by explorer. I've made a successful test with an hardcoded .ico file. Code look like this (simplified):

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

Now I try to get iconStream from "wrapped.exe". I've tried to replace:

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

with:

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

but I only get a 32*32 icon.

How to extract exactly the same .ico file (including all formats, for example with 16*16 32 bits BMP, 32*32 32 bits BMP, 48*48 32 bits BMP, 64*64 32 bits BMP and 256*256 32 bits PNG) as the one used to create 'wrapped.exe'?


Solution

  • This is very easy using IconLib. The response was already in this question: Thx to @Plutonix!

    With following helper function (of course, extraction icon file name will not be hardcoded):

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

    We just have to replace:

    File.OpenRead(@"wrapped.ico")
    

    with

    GetIconStream_ExtractIconUsingIconLib("wrapped.exe")
    

    This gives us full solution:

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