3

I have a .dll I'm trying to embed as a resource inside an executable. The following two questions are somewhat helpful, but are not a full help:

Embedding assemblies inside another assembly

This doesn't seem to work as written; the args.Name cannot be used as written, but even if it's fixed, the program still complains of a missing .dll, indicating that the assembly is not properly loaded.

Embedding DLLs in a compiled executable

and the link in one of the answers of:

http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/

However, there is no "App.xaml*" file in my project of any sort - I'm not using WPF; I'm using WinForms for my executable (changing isn't really an option, due to the nature of the executable).

I'm therefore looking for a complete set of instructions for embedding a class library in an executable as a resource and loading that .dll from the resource, without needing a .dll file outside of the embedded resource.

For example, would it be practical to simply add an "App.xaml" file to a WinForms project, or would there be negative interactions I'm not aware of?

Thanks.

Edit: This is what I'm currently using:

/// <summary>
/// Stores the very few things that need to be global.
/// </summary>
static class AssemblyResolver
{
    /// <summary>
    /// Call in the static constructor of the startup class.
    /// </summary>
    public static void Initialize( )
    {
        AppDomain.CurrentDomain.AssemblyResolve +=
            new ResolveEventHandler( Resolver ) ;
    }


    /// <summary>
    /// Use this to resolve assemblies.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    public static Assembly Resolver( object sender, ResolveEventArgs args )
    {
        Assembly executingAssembly = Assembly.GetExecutingAssembly( ) ;
        if ( args.Name == null )
            throw new NullReferenceException(
                "Item name is null and could not be resolved."
            ) ;
        if ( !executingAssembly.GetManifestResourceNames().Contains( 
                "Many_Objects_Display.Resources." +
                new AssemblyName( args.Name ).Name.Replace( ".resources", ".dll" ) )
            )
            throw new ArgumentException( "Resource name does not exist." ) ;

        Stream resourceStream =
            executingAssembly.GetManifestResourceStream(
                "Many_Objects_Display.Resources." +
                new AssemblyName( args.Name ).Name.Replace( ".resources", ".dll" )
            ) ;
        if ( resourceStream == null )
            throw new NullReferenceException( "Resource stream is null." ) ;
        if ( resourceStream.Length >  104857600)
            throw new ArgumentException(
                "Exceedingly long resource - greater than 100 MB. Aborting..."
            ) ;

        byte[] block = new byte[ resourceStream.Length ] ;
        resourceStream.Read( block, 0, block.Length ) ;

        Assembly resourceAssembly = Assembly.Load( block ) ;
        if ( resourceAssembly == null )
            throw new NullReferenceException( "Assembly is a null value." ) ;
        return resourceAssembly ;
    }
}
Community
  • 1
  • 1
Narf the Mouse
  • 1,541
  • 5
  • 18
  • 30

1 Answers1

5

You need to put the code in your main entry point. Something like this:

class Program
{
  static Program()
  {
     AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
  }

  static void Main(string[] args)
  {
    // what was here is the same
  }

  static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
  {
      // the rest of sample code
  }

}

You can't just add an App.xaml file to a windows forms application.

Also that sample code for CurrentDomain_AssemblyResolve is bizarre, I would try this code first. I haven't tested it but it looks more like the code I've used before.

Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • Thought so; hence why I asked. Also, aside from putting the code in a helper class, that's exactly what I did. – Narf the Mouse Jun 07 '12 at 07:46
  • @NarftheMouse That works too. You just have to make sure you subscribe to the `AssemblyResolve` event before the runtime tries to load your assembly. – Mike Zboray Jun 07 '12 at 07:51
  • Theoretically true; practically speaking, it appears to do that before Pogram.Program() is called. Or should I have unloaded the .dll from the references? – Narf the Mouse Jun 07 '12 at 07:55
  • @NarftheMouse by "your" assembly I mean whatever assemblies you are embedding. Obviously the runtime must load the entry point assembly before that class's static constructor runs, but in my sample the static ctor should run before any code in `Main`. – Mike Zboray Jun 07 '12 at 08:05
  • I understand all that. Here's the sequence: I add the resolver to the AppDomain.CurrentDomain.AssemblyResolve eventhandler. I run it through the debug to test; all the resolver calls that are started, complete successfully, with a non-null assembly. I copy the compiled executable, but not the .dll, into another directory (the executable and .dll being the only content). I then run the executable. It does not successfully start. The only difference is the lack of a .dll file. I will add my AssemblyResolver class to my question. – Narf the Mouse Jun 07 '12 at 08:18
  • It is, in short, the code from the first question, with null checks (probably an excessive number, and lacking try-catch-finally blocks) and with name modification. – Narf the Mouse Jun 07 '12 at 08:32
  • First problem is, it wants to load "$ProjectName.Resource.$ResourceName.resources.dll"; hence the messing around with strings. Easily enough fixed, though. Second, the executable still doesn't run solo. I also tried your code (with only the name concatenation changed to match that in the list of resource names) directly in Program.Program. Doesn't work; no error message. – Narf the Mouse Jun 07 '12 at 08:44
  • ...And then all of a sudden it stopped adding ".resources" onto the name and gah!, but, at least now it works, with a little more name editing. Thanks. – Narf the Mouse Jun 07 '12 at 09:03