Jeffrey Richter: Excerpt #2 from CLR via C#, Third Edition


Jeffrey Richter: Excerpt #2 from CLR via C#, Third Edition

  • Comments 71

Good morning, Jeffrey Richter here. My new book, CLR via C#, Third Edition (Microsoft Press, 2010; ISBN: 9780735627048), should be available via online retailers by February 15.  (Microsoft Press will alert you here when it is.)

Today I thought I’d share a section a section from Chapter 23, “Assembly Loading and Reflection,” with you. This section discusses how to embed your application’s dependent DLLs inside your EXE file, simplifying deployment by allowing you to distribute just one physical file.

> > > > >

Many applications consist of an EXE file that depends on many DLL files. When deploying this application, all the files must be deployed. However, there is a technique that you can use to deploy just a single EXE file. First, identify all the DLL files that your EXE file depends on that do not ship as part of the Microsoft .NET Framework itself. Then add these DLLs to your Visual Studio project. For each DLL file you add, display its properties and change its “Build Action” to “Embedded Resource.” This causes the C# compiler to embed the DLL file(s) into your EXE file, and you can deploy this one EXE file.

At runtime, the CLR won’t be able to find the dependent DLL assemblies, which is a problem. To fix this, when your application initializes, register a callback method with the AppDomain’s ResolveAssembly event. The code should look something like this:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {

   String resourceName = "AssemblyLoadingAndReflection." +

      new AssemblyName(args.Name).Name + ".dll";

   using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {

      Byte[] assemblyData = new Byte[stream.Length];

      stream.Read(assemblyData, 0, assemblyData.Length);

      return Assembly.Load(assemblyData);

   }

};

Now, the first time a thread calls a method that references a type in a dependent DLL file, the AssemblyResolve event will be raised and the callback code shown above will find the embedded DLL resource desired and load it by calling an overload of Assembly’s Load method that takes a Byte[] as an argument.

 

Jeffrey Richter (http://Wintellect.com)

  • Hi,

    How do you manage different assembly version?

    For example:

    Executable need DLL_1_V1 and a DLL which references DLL_1_V2.

    I tried to apply this method but in this case it doesn’t work. The 2 dll’s are correctly loaded in the AssemblyResolve event, but calls to their methods are not correct.

    Any idea how to fix this ?

  • For who getting FileNotFoundException :

    Register AppDomain.CurrentDomain.AssemblyResolve event on static constructor of Program class. It is early enough

  • What about unmanaged c++ Dlls?

    I get an error complaining about loading mixed-mode assemblies.

  • I have added all the dlls as resources in a class library. Where do I write the code that mentioned above to make it work.

    Thanks for the help.

  • Hello, it should be noted that this method prevents from registering the assembly using regasm, as it obviously does not look in the embedded resources when resolving references. Jan

  • Good Article.

    But I have a problem and I don't know how to solve.

    I followed all steps to implement this but every time I run the program my eventhandler in FORM_LOAD got an error saying "The Specified Module Could Not Be Found" the error found in my event "MyFunctionFromDLL.KeyPress += Sample_KeyPress;"

           private void Sample_KeyPress(object sender, KeyPressEventArgs e)

           {

               if (e.KeyChar == (char)Keys.Return)

               {

               }

           }

    Please help.

    Thank you in advance.

  • I see several other people saying they can not get this to compile, and asking for details on where to place this. But there is no response to their posts. I am having the same problem. While very grateful for the free advice, there isn't enough specificity for some of us to be able to make use of this.

  • It should be noted that when using this solution you cant ngen those embeded dll's.

  •    using System;

       using System.Reflection;

       internal static class EmbeddedAssembly

       {

           internal static void Load(string nameSpace, string assemblyName)

           {

               AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>

               {

                   String resourceName = nameSpace + "." + assemblyName + ".dll";

                   using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))

                   {

                       Byte[] assemblyData = new Byte[stream.Length];

                       stream.Read(assemblyData, 0, assemblyData.Length);

                       return Assembly.Load(assemblyData);

                   }

               };

           }

       }

    ok call the load method before your app uses the assembly, arguements are your project's namespace and the name of the assmebly

  • in my windows app i called here...

    namespace MyNamespace

    {

       using System;

       using System.Windows.Forms;

       using CaseStackAdminApp.Properties;

       static class Program

       {

           /// <summary>

           /// The main entry point for the application.

           /// </summary>

           [STAThread]

           static void Main()

           {

               Application.EnableVisualStyles();

               Application.SetCompatibleTextRenderingDefault(false);

               EmbeddedAssembly.Load("MyNamespace", "MyAssembly");

               Application.Run(new MainForm());

               Settings.Default.ExitTime = DateTime.Now;

               Settings.Default.Save();

           }

       }

    }

    enjoy

  • Oh lawd... GetManifestResourceStream?  When the IDE will make a strongly typed (well, byte[]) property for it if you add it as a Resource via the project's properties?  Anyhow, if you happen to catch this, was wondering about the load context and this method.  Load contexts are probably the least understood, worst documented, and most opaque parts of the framework.  Would love to hear your explanation of the ramifications of this method, and load contexts in general.

  • How do you use the dll's in WPF xaml Afterwards ?

  • As far as I know, IL Merge (1) makes total size of an assemblies much less and (2) makes assemblies to load faster. What about this? Am I right that this solution does not bring such advantages?

  • This may have just saved me massive ammounts of code trying to deal with conflicting dependancies of the same library.

    Huge Thanks.

  • I implemented this approach (as mentioned above) for conflicting version dll's and it is so far working (still in testing)

    My scenerio involved a COM component coded against multiple versions of the same software where each version will only work to automate one specific version (even though the COM class was coded to be compatible the component explicitly throws exceptions when the interop library is built against a different version, newer or older)

    In my case I have an option in the GUI for the user to select a specific Version and I built multiple version of the interop library, one against each version of the com component. The "correct" one is loaded based on user selection.

    I also precompressed the DLL's using a GZipStream object to reduce the total size of the DLL, which works very well as you simply decompress it on demand which takes < 1 sec.

    One last thing I would say is some posters indicate you cannot do this or is difficult to implement in cases with "EXE-DLL-DLL" (DLL with embedded DLL) but it's quite simple in VB...

    Public Module modMain

       Public WithEvents CurDomain As AppDomain = AppDomain.CurrentDomain

       Private Function AssemblyResolve(sender As Object, args As System.ResolveEventArgs) As System.Reflection.Assembly Handles CurDomain.AssemblyResolve

           .... ' resolve assembly here based on GetExecutingAssembly or return nothing...

       End Function

    End Module

Page 4 of 5 (71 items) 12345
Leave a Comment
  • Please add 5 and 1 and type the answer here:
  • Post