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)

  • 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?

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

               };            

           }

  • 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

  • Hi there,

    i recently had the problem in deploying an application which is described as following:

    social.msdn.microsoft.com/.../129977c3-0edc-4f37-9071-2116b347464b

    If i just add the two ".dlls" as "Embedded Resource"-Files if i cannot use the contained classes/functions.

    So do i have to add the two dlls additionaly as project reference to use the containted classes/functions?

    I also tried to set "Copy Local" to "False" on those ProjectReferences, then the project will compile fine

    and wont produce additional ".dll" files but when starting it will crash with an error like this:

    Der Typ "WpfControlLibraryTestResources.UCFromControlLibrary" kann nicht gefunden werden. Die während der Kompilierung verwendete Assembly unterscheidet sich möglicherweise von der beim Laden verwendeten Assembly, und der Typ fehlt.  Fehler in Objekt "System.Windows.Controls.Grid" in Markupdatei "SimpleClassLibraryUsingResources;component/showresourcecontrol.xaml"

    (sorry for the german translation)

  • 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.

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

  • Hi, Locarno. Jeffrey says, "The answer is 'No'."

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

  • I assume that this works for C# only, not any version of VB.Net.  Is this correct?

    "VB.Net, the other C#"

  • 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

  • Great tip thx! But isn't this a bit complicated code? All you need to do is add a dll file via Project->Properties->Resources so VS generates the access classes and then you can load those assemblies in one line:

    using R = MyProject.Properties.Resources;

    ....

    ....

    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => Assembly.Load(R.MyMergedDll);

    Also be aware of .NET 4 breaking change. Starting with .NET 4, the AssemblyResolve callback is called for EVERY unresolved assembly including resources etc, so the above code is not really correct, some target name checking and loaded assembly caching should be used...

  • 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?

  • @Devon

    I would be interested on your opinion on this

    code.google.com/.../HowItWorksEmbedTask

    It uses Jeffrey Richters approach in a seemless way.

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

  • Many thanks,

    After an hour or two with ILMerge that then didn't work, this was wonderfully simple.

    John

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