Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[.NET] Merging .NET components

• ♠️​ ♦️ ♣️ ​♥️ •
Joined
Mar 25, 2012
Messages
909
Reaction score
464
Merging .NET components

Version: 1.1
Author: שเ๒єtгเ๒є (vibetribe)
Tested with: Visual Studio 2012
Language: C# (and VB.NET)



Table of contents

1. Introduction
1.1 Problem analysis
1.2 Most used unsuitable solutions
1.3 Specific example

2. Embedding components while building
2.1 Reference components
2.2 Embedding referenced components
2.3 Resolve assemblies
2.4 Call assembly resolve event before component usage

3. Update embedded components automatically
3.1 Update components from own solution
3.2 Update components from extern solution

4. Access embedded components over embedded components
4.1 Cross-references
4.2 Cross-forwarding
4.3 Renounce useless merging

5. Duplicate component behavior
5.1 Storage behavior
5.2 Loading behavior



1. Introduction

I'm happy to introduce a detailed tutorial about .NET component merging here at RaGEZONE. This tutorial took me about 3 days of researching work through the deepest edges of the internet, and it was composed by many different pieces of information.
This tutorial will not only explain how to get this procedure done, it will even explain what happens behind the scene and what's the advantages and disadvantages of the different layouts as well as alternatives.
Please keep in mind, this is addressed to people who want to learn it, not just use it.

For all VB.NET users. This tutorial works for you as well as for C# users. However, all provided codes and templates are shown in C#. To get around this, use any C#-VB.NET converter, since both can be translated 1:1.


1.1 Problem analysis

In the development progress of .NET components and .NET applications many people lead to solutions with many projects and many external .NET components that will fill the output directory with several DLL files by using many references on build time. To avoid such file salads to customers/clients we are trying to find an effective way to merge DLL files in EXE files. We also need a way to merge DLL files in other DLL files in case of providing a DLL component for the customer/client he/she can work with. Any produced merged components should still stay fully compatible with other .NET components, so have no wrapper that changes the MSIL to something incompatible, no matter it will just be a class library, console application, windows forms application or WPF application.

This tutorial leads to a solution that will make all this done without forcing to repeat any manual steps when updating your code. So it will allow you to implement it on your personal visual studio solution that contains your projects, and it will execute such steps for you automatically when building your application on debug or release build.



1.2 Most used unsuitable solutions

Most people counting for after-solutions. Meaning, you take your components and run any program over it to merge them. The most known tool is might ILmerge, but even .NetZ is common.
Problem on both solutions is the incompatibility to WPF applications, they will not start after merging, because they are unable to find the XAML resources in the merged components. We will avoid such WPF problems, even when the embedded assemblies contain other WPF components such as UserControls or CustomControls.
Also .NetZ will use an own wrapper that makes your components inconsistent to other .NET components until they are packed the same way.
A statement of the programming author of ILmerge includes that he would never have programmed ILmerge when he had heard about the better way to merge components within Visual Studio.


1.3 Specific example

To explain how merged components can be merged another time this tutorial will orient on the following solution structure:

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
The targeting aim is
  • to reference MergeExample.ClassLibrary_A from MergeExample.ClassLibrary_B and supply MergeExample.ClassLibrary_A.dll into MergeExample.ClassLibrary_B.dll
  • to reference the merged MergeExample.ClassLibrary_B from MergeExample.WindowsFormsApplication_C and MergeExample.WpfApplication_C, so supply MergeExample.ClassLibrary_B.dll (that already contains MergeExample.ClassLibrary_A.dll) into MergeExample.WindowsFormsApplication_C.exe and MergeExample.WpfApplication_C.exe while having the namespace MergeExample.ClassLibrary_B visible in it.
  • to update embedded components when changing it's source, automatically
  • to make the namespace MergeExample.ClassLibrary_A visible in MergeExample.WindowsFormsApplication_C and MergeExample.WpfApplication_C, additionally
Intentionally I do not have any console application in my example solution due the fact implementing the merge system to a console application is exactly the same like implementing the merging process to a windows forms application.
Using this on windows services is easy, too. But I bet you will get it yourself when you are able to program windows services, already.



2. Embedding components while building


2.1 Reference components

To be able to use references at all, you have to add the references in your solution browser by right clicking on the reference folder of your project and adding your components you want to use in the specific namespaces.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
At the end, you want to see only one DLL or EXE in your output directory, so right click all your added references that do not belongs to the .NET framework and edit it properties in the property window. Set Local copy to false on the reference assemblies you want to merge later.

To continue you need the first built component you want to merge into another component. In this case we need the produced DLL of MergeExample.ClassLibrary_A. So right click on the project in your solution browser and select Create.


2.2 Embedding referenced components

Now create a directory named Internal Libraries in your project that will have embedded components. It is very important to name it only that way, because it will be required for later steps to update it, automatically.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
Right click the folder and add an existing file, browse to your components you want to merge in it.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
Right click your added DLL components and enter the property window. Make sure the Build behaviour is set to Embedded resource.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
Build this project now and you will see a nice result, just one DLL file that contains another as reference. This DLL can be used to be merged to other projects again, so I continue this with my application projects too. So I got a double merged component in a single merged component in an end component.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
The final solution looks like above in your solution browser. Keep in mind this even works with third party components that are written in .NET. (The screenshot is outdated, do not trust in the folder name Internal Library, the right name is Internal Libraries)


2.3 Resolve assemblies

However, when executing your program or referencing a merged DLL component you will lead into a System.IO.FileNotFoundException because the framework is unable to resolve the assembly, because it's simply missed in the components directory. In any case the component will search in the directory for the missing component as first. So even when we get a work-around people can place compatible DLLs in the folder to bypass the internal loading of the components.

In every project that contain Internal Libraries you need a ModuleInitializer class that will help you to resolve the missing assemblies, the class name is important here, too.
For your easiness I created a template file for you, you can easily put into %userprofile%\Documents\Visual Studio 2012\Templates\ItemTemplates\Visual C#\:
Download:
(Do not extract this file, Visual Studio expects ZIP files in the above directory.)

After adding the file, please restart Visual Studio.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
Then create a new item from the template in the component that contains the Internal Libraries. Do not forget to remove the '1' inside the name before adding the item.
Source Code:
[paste]ZhNnyMfW[/paste]
As you can see ModuleInitializer.Run() will subscripe to the resolve event of current domain. This event will be called by .NET on execution time after the failing to load assemblies into the current domain on runtime. This is exactly what happens when the specific assemblies are missed in the directory.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
After adding the ModuleInitializer to all your projects that will merge other components, your solution browser should look like above.


2.4 Call assembly resolve event before component usage

To be sure you subscribe the resolve event early enough there are specific solution cases.

On applications of any kind you just have to call ModuleInitializer.Run() on the static constructor in your entry class of your program. Placing it anywhere else like the Main function will be too late.

PHP:
static class Program
{
	static Program()
	{
		ModuleInitializer.Run();
	}
}
Program.cs on windows forms applications or console applications.

PHP:
public partial class App : Application
{
	static App()
	{
		ModuleInitializer.Run();
	}
}
App.xaml.cs on WPF applications

On DLL components you will lead into a big problem, because the .NET framework does not provide any way to start any code before loading associated components on run time.
For this you have to inject your DLL-components that contain any Internal Libraries on the post build event (after build). This behavior can be done with the InjectModuleInitializer.exe.
Download:
Virus Total:

Place this EXE file into the root folder of your solution.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
Now go into the properties of your project and navigate to the Build events tab.
Code:
..\..\..\InjectModuleInitializer.exe $(TargetName)$(TargetExt)
Paste this code into the post build event. When compiling your merged DLL component it will inject a call to ModuleInitializer.Run() early enough, so on run time it will be done before trying to load any associated assemblies.

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
In order to give third persons a quick understanding it might be useful to add this EXE to your solution in the solution browser.

Now your solution projects are able to build and run successfully.



3. Update embedded components automatically

It is usual to be forced to update references of third party components, manually. However, in this case even your own embedded components will need to be re-added when changing it's source and building again. So in our example I would be forced to re-add MergeExample.ClassLibrary_A.dll in the Internal Libraries folder of MergeExample.ClassLibrary_B when changing and building the source of MergeExample.ClassLibrary_A.
To automate this process I've wrote a tool called UpdateInternalLibraries.exe
Download:
Virus Total:
Source Code:
[paste]7hF9TX8R[/paste]

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
Place the file into your solutions root directory as you did with the InjectModuleInitializer.exe and add it into your solution in your solution browser when desired.


3.1 Update components from own solution

In order to update the components into your Internal Libraries folder before building your project open it's project properties and navigate to the Build Event tab.
Code:
..\..\..\InternalLibrariesUpdater.exe
Add the code to your prebuild event. The tool will now check you debug or release and search for any components that are located in your Internal Libraries in the output directory of your other intern projects in your solution and overwrite them to your Internal Libraries directory before building and merging your components.


3.2 Update components from extern solution

There is a case you embedded a component from another solution on your drive. In this case you can provide an additional parameter to search in another solution directory for the build components you want to update into your project that merges those. This parameter should navigate to the other solution.
Code:
..\..\..\InternalLibrariesUpdater.exe ..\..\..\..\OtherSolutionName
This example uses a relative path that is recommend when working with teams with any SVN system. It assumes all solutions are in the same directory at every programmer participant you share your development files with.

Please keep in mind to use both methods (so two calls to InternalLibrariesUpdater.exe) when having intern and extern solution components embedded into your project.



4. Access embedded components over embedded components

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
The typical behavior of Visual Studio says in the above example that you will not be able to view the namespace of LibA in the project AppC. This stays the same when using embedded assemblies. However there are some work-arounds to get this done.


4.1 Cross-references

The classic work-around is simply as it is to add a reference to LibA in AppC. This never leat to any problems due the fact LibB has a reference to LibA, so LibA.dll was in the same directory in any case.
When using embedded components in LibB and AppC this way would produce a side effect that is very controversial in my eyes: It would store the assembly LibA two times in AppC.
By using huge amounts of embedded duplicated components in many hierarchic project levels this can increase the final application size a lot.


4.2 Cross-forwarding

You can write an interface (not the typically interface in Visual Studio) to allow AppC to access all needed components in LibA by providing inherted classes of LibA in LibB as well as providing properties and methods in LibB that access code in LibA and forward your parameters as well as return values.


4.3 Renounce useless merging

Unless you need LibB as single DLL file that contains it's associated components, do not do it. The above examples are not perfect, it would be more recommend to let only the final application merge all components instead.



5. Duplicate component behaviour

שเ๒єtгเ๒є - [.NET] Merging .NET components - RaGEZONE Forums
In the above example we merge associated components in LibA, LibB and AppC. We decided for a Cross-reference to be able to see the namespace of LibA in AppC. We even got another library called LibX that is embedded in LibA and LibB with different versions.


5.1 Storage behavior

The above example will lead your application AppC.exe to have the following assemblies stored in:
  • 1 x AppC
  • 1 x LibB
  • 2 x LibA
  • 2 x LibX (1 x v1.0.0.0 and 1 x v2.0.0.1)
This shows how fast your application can grow when using embedded components on all your projects.


5.2 Loading behavior

To your luck the above example will not force your application to load LibA two times into your memory on run time. But this seemingly positive effect got a bad shadow side: Your application will only load one assembly of both LibX assemblies into your memory. It will load the assembly LibX v2.0.0.1 into the memory on run time due the fact it's embedded on a higher stage in the hierarical tree. The version will not affect this decision, because it's simply load as first in the program code. If you would switch both assemblies it would prefer the older LibX in this case. This problem will force LibC to use a wrong version of LibX, so it will throw you an exception on conflicts when accessing LibX from LibC.
The conclusion is to avoid version conflicts at all. Even using extern alias key words or trying to change the AppDomain of the loading components will not help you there.


-----


I hope this tutorial is useful for all our .NET coders here. Feel free to ask questions and leave me a feedback.
 
Last edited:
• ♠️​ ♦️ ♣️ ​♥️ •
Joined
Mar 25, 2012
Messages
909
Reaction score
464
Last time I checked, ILMerge was able to merge a WPF application?

No, it's not. I have tested it by myself.
Also, when you google for ILmerge WPF you will consider a lot of problems threads. There are might any alternatives (I could not get work after hours), but they require more effort than the embedded resources method I explain about in this tutorial.
 
Newbie Spellweaver
Joined
Oct 17, 2013
Messages
71
Reaction score
43
ILMerge will not work with a WPF application.

Great tutorial, however I may be incorrect, but with your method WPF would it not load the xaml first before running the initialiizer method? Which would then cause issues should you have any embedded dll that is contains any resource dictionaries required for your xaml.

I personally prefer to toss out the rarely used App.xaml and create my own entry point class for which to hook in the embedded dll loading. Like so:

PHP:
using System;
using System.IO;
using System.Windows;

namespace MyProgram.app
{
    public partial class App: Application
    {
       public void InitializeComponent()
        {
            this.StartupUri = new System.Uri("MainWindow.xaml", System.UriKind.Relative);
        }

       private void InitEmbeddedDllLoading()
        {
            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                // DLLs are embedded under the Resources directory 
                var name = typeof(App).Namespace + ".Resources." + new System.Reflection.AssemblyName(args.Name).Name + ".dll";

                using (var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
                {
                     Byte[] assemblyData = new Byte[stream.Length];
                     stream.Read(assemblyData, 0, assemblyData.Length);
                     return System.Reflection.Assembly.Load(assemblyData);
                }
            };
        }

        [System.STAThreadAttribute()]
        public static void Main()
        {
            var app = new App();
            app.InitEmbeddedDllLoading();
            app.InitializeComponent();
            app.Run();
        }
    }
}
 
• ♠️​ ♦️ ♣️ ​♥️ •
Joined
Mar 25, 2012
Messages
909
Reaction score
464
Great tutorial, however I may be incorrect, but with your method WPF would it not load the xaml first before running the initialiizer method? Which would then cause issues should you have any embedded dll that is contains any resource dictionaries required for your xaml.
I can answer your question with no: It's able to find XAML resources in the embedded resources, too, due the fact the XAML resources will be load after the ModuleInitializer.Run(). The trick is to put ModuleInitializer.Run() in the static constructor of the entry class. Inside the Main function it's already too late.
 
Last edited:
Back
Top