Compartilhar via


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

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 ( https://Wintellect.com )

Comments

  • Anonymous
    February 03, 2010
    How does this compare to ILMerge?  Any advantages to one over the other?

  • Anonymous
    February 03, 2010
    Jeffrey Richter, you rock!  I still have Applied .NET Framework Programming on my bookshelf at work and CLR via C# 2 on my bookshelf at home.  Can't wait for CLR via C# 3 to come out.  

  • Anonymous
    February 03, 2010
    David, here's a response from Jeff: ILMerge produces a new assembly file from a set of existing assemblies. This means that the original assemblies lose their identity (name, version, culture, and public key). What I am showing here is creating an assembly that embeds the EXISTING assemblies into it so that they do not lose their identity at all. Because the original assemblies keep their identity, more scenarios work correctly, such as serialization & security. ILMerge is most useful when all the assemblies being merged are produced by a single organization/company. What I propose can be used with assemblies produced by different organzations/companies.

  • Anonymous
    May 24, 2010
    I've just implemented this on my current project and it's saved loads of installation and build hassles. Cheers!!

  • Anonymous
    July 28, 2010
    What if I don't have an EXE and instead I am building a library? How do I achive the same results for a DLL. I'm wondering what would be an entry point for a DLL where I could attach do AssemblyResolve event.

  • Anonymous
    July 28, 2010
    The comment has been removed

  • Anonymous
    November 15, 2010
    As the author of ILMerge, I think this is fantastic! If I had known about this, I never would have written ILMerge. Many people have run into problems using ILMerge for WPF applications because WPF encodes assembly identities in binary resources that ILMerge is unable to modify. But this should work great for them. Thanks!

  • Anonymous
    November 15, 2010
    As the author of ILMerge, I think this is fantastic! If I had known about this, I never would have written ILMerge. Many people have run into problems using ILMerge for WPF applications because WPF encodes assembly identities in binary resources that ILMerge is unable to modify. But this should work great for them. Thanks!

  • Anonymous
    November 27, 2010
    thanks for the tip, it works ONLY for a Full Trust Application, also for WPF (where ILMerge fails to merge libraries) what about the following scenario: I want to build WPF for Browser Application (with Partial Trust, because i don't want not to play around with installing certificates on client machines - this app should widely accessible). Furthermore, i want to obfuscate my exe (including these libraries from resource), and i am not sure, that obfuscation software can obfuscate resources (at least Eazfuscator, which i use is not doing this).

  • Anonymous
    December 05, 2010
    I second that, especially the issue with obfuscator. Any hints?

  • Anonymous
    December 05, 2010
    I think for obfuscator, you can simply embemed the obsfucated dlls....

  • Anonymous
    December 29, 2010
    I am trying to use the above in my application I threw AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve) is that correct?

  • Anonymous
    December 30, 2010
    I tried and also ran into a problem with this pop-up message: TestPortfolioDragDrop [name of .exe] has stopped working. I put the code in the constructor of MainWindow of a WPF Appliation called TestPortfolioDragDrop.

  • Anonymous
    January 03, 2011
    Hi, How/Where can I register the eventhandler early enought? If I register it in my Main()-Function it's too late because the CLR already checked the DLL-Reference before it calls my Main-Function and so it fails to load. I'm using C#/.Net2.x (vs2005) yours hr

  • Anonymous
    January 03, 2011
    OK, I found the solution: The function calling the DLL must reside in another class, then it will be loaded later. Now everything works. yours hr

  • Anonymous
    January 13, 2011
    I tried to use this method with an application that uses XmlSerializer and It is trying to load mscorlib.XmlSerializers Do I need to generate the xml serializer for the class?

  • Anonymous
    January 24, 2011
    To get this working in a DLL you need to register for the AssemblyResolve event from a module initializer.  This is not available directly from C# projects but suitable IL can be injected as an MSBuild or post-build step.  We've implemented that solution to dynamically load the correct version of an internal assembly based on processor architecture at runtime and it works very well. Module initializers: blogs.msdn.com/.../494914.aspx Source and an executable that does the module injection: tech.einaregilsson.com/.../module-initializers-in-csharp    internal static class XdkInterop    {        private const string XdkPlatformInteropAssemblyName = "xdevkit"; ...        /// <summary>        /// This method is injected into the built assembly as a module initializer by a post-build        /// target defined in the .csproj file.  Code running here can register for the        /// AssemblyResolve event in time to handle the module loading we need.        /// </summary>        internal static void Resolve()        {            AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs resolveEventArgs)            { ...                return Assembly.LoadFrom(assemblyPath);            };                    }

  • Anonymous
    January 24, 2011
    Hi Jeffrey, I need to merge Telerik dlls for silverlight into a single dll. I tried to use ILMerge. But it is not working for me. So I thought of using the above method mentioned by you. Updating the build action to "Embedded Resource" and use the AppDomains AppResolver event. I have also gone through the following url for Module Initializer. tech.einaregilsson.com/.../module-initializers-in-csharp I have created a Silverlight class library and added the required Telerik dlls. I tried to modify the build action for the "Telerik dll's". But I didn't find the Build Action property in the properties window. How can I change the build action property for the Telerik dlls. Also from the above discussions I understand that making the build action will embed the dlls in to an exe and we need to use a callback method to invoke the methods in the embedded assemblies. Is this works only with the exe? What are all the changes I need to do, for making a single dll using the above method? Please help me by providing steps, to embed the Telerik dlls for Silverlight into a single dll. Thanks in advance, SPB

  • Anonymous
    January 25, 2011
    The comment has been removed

  • Anonymous
    February 14, 2011
    I am struggling with this just a little bit.  If I have a windows forms app written in C#, where do I add this code to my application?  I tried adding it to the beginning of Main() in my Program.cs file, but this still produces errors about missing libraries.  Any help would be appreciated, as I think this would make distribution of my tools a lot easier.

  • Anonymous
    March 13, 2011
    Is there a way to do this with project references within one solution, rather than DLL references?

  • Anonymous
    March 14, 2011
    Hi, Locarno. Jeffrey says, "The answer is 'No'."

  • Anonymous
    March 14, 2011
    Locarno, Jeffrey just added this: "It is a Visual Studio limitation that disallows this."

  • Anonymous
    March 29, 2011
    I assume that this works for C# only, not any version of VB.Net.  Is this correct? "VB.Net, the other C#"

  • Anonymous
    June 26, 2011
    So any thoughts on how I embed the associated pdb file for a given dll and have it be recognized in a stack trace? Thanks in advance

  • Anonymous
    July 22, 2011
    The comment has been removed

  • Anonymous
    August 05, 2011
    How to make it so the debug version of the dll is embedded when building the main exe in debug and vice versa in release?

  • Anonymous
    August 10, 2011
    @Devon I would be interested on your opinion on this code.google.com/.../HowItWorksEmbedTask It uses Jeffrey Richters approach in a seemless way.

  • Anonymous
    August 18, 2011
    Great article.. saved my time..Note if you are using it in a class library just call the assembly resolve in the default constructor..

  • Anonymous
    August 22, 2011
    Many thanks, After an hour or two with ILMerge that then didn't work, this was wonderfully simple. John

  • Anonymous
    August 28, 2011
    Simon Cropp, Jeff has the following response for you: I have used Einar Egilsson's suggestion myself with great success. I have not tried the MSBuild task myself, but this seems like a great way to go. --Jeffrey Richter (http://Wintellect.com)

  • Anonymous
    September 23, 2011
    Hi Jeff, I am not sure if I understand how exactly to implement this? In the sense that I already have other methods dependent on these assemblies. Kindly help as I am new to this. Thanks John

  • Anonymous
    September 26, 2011
    John Morgan, Jeffrey writes this in response: I have an EXE that depends on DLL1 and DLL1 depends on DLL2. DLL2 requires no special code. In DL1, I add the following code plus whatever I would normally put in DLL1:   internal static class ModuleInitializer {      // See tech.einaregilsson.com/.../module-initializers-in-csharp      // and code.google.com/.../HowItWorksEmbedTask      internal static void Run() {         AppDomain.CurrentDomain.AssemblyResolve += ResolveEventHandler;      }      private static Assembly ResolveEventHandler(Object sender, ResolveEventArgs args) {         String dllName = new AssemblyName(args.Name).Name + ".dll";         var assem = Assembly.GetExecutingAssembly();         String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));         if (resourceName == null) return null; // Not found, maybe another handler will find it         using (var stream = assem.GetManifestResourceStream(resourceName)) {            Byte[] assemblyData = new Byte[stream.Length];            stream.Read(assemblyData, 0, assemblyData.Length);            return Assembly.Load(assemblyData);         }      }   } For the DLL1 project, I have the following post-build event (for InjectModuleInitializer.exe, see the comments above):      $(ProjectDir)InjectModuleInitializer.exe $(TargetPath) Then in my EXE, I have the same ResolveEventHandler method and in Main, I have this:      AppDomain.CurrentDomain.AssemblyResolve += ResolveEventHandler; --Jeffrey Richter (http://Wintellect.com)

  • Anonymous
    September 28, 2011
    Hi Jeff, Thanks for the prompt reply. What is the function of DLL2? Is it where the external assemblies have been used? Regards John

  • Anonymous
    September 29, 2011
    John Morgan, Jeffrey says: DLL2 is just dome library that DLL1 depends on; your app may not have a DLL2. The solution I describe allows you to package DLL1 and what it depends on (DLL2) together into the EXE and have everything work even if the EXE doesn’t depend on DLL2 directly. --Jeffrey Richter (http://Wintellect.com)

  • Anonymous
    October 02, 2011
    Hi Jeffrey, I would like to impliment your approch of loading and resolving DLL from application resource, but I am a starter, I don't know C#. Is it possible to port this code to VB (.Net 4.0), if possible please post the code, that would be helpfull for many programmers like me. Thanks in advance, Nirmal Rajmohan

  • Anonymous
    October 24, 2011
    I just did this in Vb.net. My Application looks like this: Class Application    Private Sub Application_Startup(              sender As Object,              e As System.Windows.StartupEventArgs) _                  Handles Me.Startup        AddHandler AppDomain.CurrentDomain.AssemblyResolve,                                                                                                             AddressOf pHandleAssemblyResolver    End Sub    Private Function pHandleAssemblyResolver(            sender As Object,            e As System.ResolveEventArgs) As Reflection.Assembly        If e.Name.Contains("HtmlAgilityPack") Then            Return Reflection.                   Assembly.                   Load(My.Resources.HtmlAgilityPack)        Else            Return Nothing        End If    End Function End Class I added "HtmlAgilityPack.dll" as a file resource. And then changed the property "Copy local" of the references to it to False.

  • Anonymous
    November 01, 2011
    Thank you so much David Sherwood. I will check this solution soon and will post the results. Can we load an ActiveX library (*.OCX) using this method? Is there any way to do that? Thanks and regards, Nirmal Rajmohan

  • Anonymous
    November 30, 2011
    If you put the AssemblyResolve event handler registration code in <Module>::.cctor (i.e., the module's .cctor method, the "global" .cctor), it should work even if it's a DLL file. This is what most obfuscators do.

  • Anonymous
    December 21, 2011
    The assembly load results should be cached, since you might have several resolve calls to the same dll. For example if a dll is referenced in <configuration><configSections> and the same dll is later referenced by code, you might have two AssemblyResolve calls. The args.Name parameter might be different, but the "new AssemblyName(args.Name).Name" will be equal, so use that as the cache key. A static Dictionary<string,Assembly> should suffice?

  • Anonymous
    February 01, 2012
    Jeffrey, I am afraid that this more and more widely pattern proposed here provokes a waste of (the so precious) process memory. If an asm A embeds and asm B as a resource, the B code will be duplicated 3 times in the process:

  1. in the A file image pre JIT
  2. such as the B assembly file image pre JIT
  3. such as the B assembly post JITed The 1) case introduce the waste. Unless I am wrong, it would be a good idea to tell this caveat to enthusiasts about this pattern, before their program memory burst.
  • Anonymous
    February 23, 2012
    Hi, I'm quite new to this but this solution is exactly what I need for deployment of my application. My problem is that I don't know where to put this in my application's code. When I paste the code into my main forms main method there are several errors. So please can you tell me where to put this code extract to make it work? yours Pete

  • Anonymous
    March 18, 2012
    I didn't notice Matty's question get answered and I have the same question. I added the code to load my dll as indicated in this post, but I'm still getting the following error and cannot compile: The type or namespace name 'ClassInDLL' could not be found (are you missing a using directive or an assembly reference?)

  • Anonymous
    March 18, 2012
    The comment has been removed

  • Anonymous
    April 03, 2012
    The comment has been removed

  • Anonymous
    May 07, 2012
    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 ?

  • Anonymous
    May 21, 2012
    For who getting FileNotFoundException : Register AppDomain.CurrentDomain.AssemblyResolve event on static constructor of Program class. It is early enough

  • Anonymous
    July 07, 2012
    What about unmanaged c++ Dlls? I get an error complaining about loading mixed-mode assemblies.

  • Anonymous
    July 23, 2012
    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.

  • Anonymous
    September 03, 2012
    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

  • Anonymous
    September 11, 2012
    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.

  • Anonymous
    October 10, 2012
    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.

    • Anonymous
      August 12, 2016
      I had the same problem and I've found my issue: my Program class had static properties based on classes from the DLLs, so they were created before the Main() was called.On my test project I don't have any static property and it works like a charm.
      • Anonymous
        August 12, 2016
        Nevermind it doesn't work.
  • Anonymous
    November 25, 2012
    The comment has been removed

  • Anonymous
    December 04, 2012
       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

  • Anonymous
    December 04, 2012
    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

  • Anonymous
    January 15, 2013
    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.

  • Anonymous
    January 16, 2013
    How do you use the dll's in WPF xaml Afterwards ?

  • Anonymous
    April 01, 2013
    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?

  • Anonymous
    April 10, 2013
    This may have just saved me massive ammounts of code trying to deal with conflicting dependancies of the same library. Huge Thanks.

  • Anonymous
    April 13, 2013
    The comment has been removed

  • Anonymous
    May 06, 2013
    I cannot get this to work. I have a simple exe: namespace Test {    class Program    {        static void Main(string[] args)        {            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);            var args = new ArgParser();        }        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)        {            String resourceName = "Test." +                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);            }        }    } } In my TestClass, I have: using ExternalDll; namespace Test {   class ArgParser    {        public Dictionary<String, String> ParsedArgs { get; set; }        public ArgParser(string[] args)        {            SomeMethod(); //defined in ExternalDll        }    } } . This will not compile, as it cannot resolve the ExternalDll reference. So how to I call 'SomeMethod()'??

  • Anonymous
    September 29, 2013
    The comment has been removed

  • Anonymous
    October 04, 2013
    Does anyone know the VB.NET alternative for this?

    • Anonymous
      July 13, 2016
      VB.Net Versionhttp://stackoverflow.com/questions/6458436/including-a-dll-as-an-embedded-resource-in-a-wpf-project/38365268#38365268
  • Anonymous
    October 23, 2013
    Great solution! In company environment using third party tool is always an eyebrow-raiser - unfortunately but understandable. A nice native solution is a lifesaver.

  • Anonymous
    November 01, 2013
    May be some one may help stackoverflow.com/.../how-to-merge-files-to-single-executable-or-decrease-the-number-of-distributed-fi

  • Anonymous
    March 13, 2014
    Jeff, you rock ! And save me a lot of trouble... Thank you very much

  • Anonymous
    April 09, 2014
    I just wanted to say thank you very much to Jeffrey Richter & Einar Egilsson. The combination of CLR injection at post-compilation event, and resource load at module initialization works perfectly for embedding several referenced DLLs into a single assembly. We can forget about future dependency and deployment problems.

  • Anonymous
    May 15, 2014
    I have an application that references about 12 dlls that are not part of the .net framework.  I would llike to include one or more of these dlls in the exe.  So, I am following Jeff's advice. I added a debug version one of these dlls to the project and set it's build option and added the AssemblyResolve event handler according to Jeffrey's blog.  The dll seemed to load and run OK, except it threw a InvalidCastException where it does not occur when the dll is loaded normally as a file.  The resource dll defines an interface and a class that implements the interface.  The exception occurs in the resource dll where an instance of the class is created and cast as the interface, such as, Type type = Type.GetType(aIDictionary["type"]); ISomeInterface interface = (ISomeInterface) Activator.CreateInstance(type); Does anyone have any advice?

    • Anonymous
      July 06, 2016
      Hi,I have the same problem: an interface defined in an embedded assembly does not cast successfully, even if the type implementing the interface is defined int he SAME assembly.... the code works if the assembly is loaded "normally" from a .dll on disk.Did this topic ever get any replies? I'm stumped.
  • Anonymous
    June 25, 2014
    Take a look at Vitevic Assembly Embedder (www.vitevic.com/assembly_embedder.html). It wraps all internals (assembly embedding & CLR injection) into Visual Studio extension. You can just set "Embed Assembly" property for any reference via Visual Studio GUI.

  • Anonymous
    October 24, 2014
    Solution to embedding DLLs in DLLs- use Vitevic Assembly Embedder: vitevic.com/assembly_embedder.html - works like a charm!

  • Anonymous
    February 03, 2015
    I have an app that uses lots of dll's, and it works with all of them except 2.  DLL1 is also being referenced by DLL2. I have the source code for DLL2, so is there anything I can do make it work?

  • Anonymous
    July 06, 2016
    Hi all,I think I have also found a little kink in using this technique, took me a while to realize what's going on, but here it is:I had a situation with a whole bunch of DLLs embedded as per this awesome technique, compiled together, several level of indirect referencing etc, and discovered that the AssemblyResolve event was fired for an assembly that had already been previously loaded and used, retrieved from the embedded resource as planned. Without modification, my handler just loaded the same assembly a 2nd time, evidently replacing the previous one. After that point, however, comparisons between objects/types originating from the 1st vs 2nd copy began to fail; namely, an object created with Activator.CreateInstance could no longer be cast as an interface (presumably referencing the earlier version).I noticed that the Name member of the ResolveEventArgs was the fully-qualified assembly name for the first resolve event, but the short name for the second. So my solution/workaround was to check if the needed assembly had already been loaded previously by comparing short names, and just return the initially loaded assembly in response to subsequent resolve events...somethinglike: static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { foreach (Assembly assy in AppDomain.CurrentDomain.GetAssemblies()) { if (assy.FullName == args.Name || assy.GetName().Name == args.Name) { return assy; } } .....It got me through my situation, maybe it'll help someone else out there.

  • Anonymous
    October 06, 2016
    Hi, this solution works great for me. Previously i worked with ILMerge untill i started adding WPFs into my Programms.Is it possible to load assemblies like ILMerge's internalize function?

  • Anonymous
    October 10, 2017
    The comment has been removed