HOWTO: Upgrade Rebex DLLs without recompiling

  |   Lukas Pokorny

With the first release of .NET Framework, Microsoft also introduced the concept of strongly-named assemblies (DLLs) in order to avoid some problems of what was known as DLL Hell. A strong name means that a DLL is uniquely identified not only by filename, but also by its public key token and version number. This makes it impossible to accidentally run an application with a different version of a DLL that is incompatible with the expected version.

In practice, this means that as far as the .NET's class loader is concerned, version 2.0.0.0 and version 2.1.0.0 of an assembly are completely different identities and therefore considered incompatible. The same applies to versions that only differ in a build or revision number. Therefore, for example, applications compiled with Rebex FTP for .NET 2.5.2800.0 or 3.0.3200.0 will refuse to work with 3.0.3300.0. This applies both to Rebex FTP for .NET installed as a shared assembly in Global Assembly Cache (GAC) and as an application-private assembly in the application folder.

The recommended way of dealing with this is to recompile your application with the new version and distribute the next version of your application with a new set of DLLs. However, there might be some scenarios where this is not desirable – and this document describes how to make your existing applications use the new Rebex component version instead of the old one without the need to recompile your code.

Compatibility List

The following table lists all currently available Rebex assemblies, their current version and all the versions it should be backward compatible with:

Assembly Compatible Version Current Version
Rebex.Net.Ftp.dll 2.0.0.0-3.0.3483.0 3.0.3484.0
Rebex.Net.Sftp.dll 1.0.0.0-2.0.3483.0 2.0.3484.0
Rebex.Net.Smtp.dll 1.0.0.0-1.0.3483.0 1.0.3484.0
Rebex.Net.Pop3.dll 1.0.0.0-1.0.3483.0 1.0.3484.0
Rebex.Net.Imap.dll 1.0.0.0-1.0.3483.0 1.0.3484.0
Rebex.Net.Ssh.dll 2.0.0.0-2.0.3483.0 2.0.3484.0
Rebex.Net.ProxySocket.dll 2.0.0.0-2.0.3483.0 2.0.3484.0
Rebex.Net.SecureSocket.dll 3.0.0.0-3.0.3483.0 3.0.3484.0
Rebex.Security.dll 1.5.0.0-1.5.3483.0 1.5.3484.0
Rebex.Mail.dll 1.0.0.0-1.0.3483.0 1.0.3484.0
Rebex.Net.SshShell.dll 1.0.0.0-1.0.3483.0 1.0.3484.0
Rebex.TerminalEmulation.dll 1.0.3479.0-1.0.3483.0 1.0.3484.0

If you currently use an older version not shown in this table, it means that it is no longer compatible with the current version. In this case, you will have to recompile your code and possibly make several slight changes to it.

The public key token for all Rebex assemblies is “1c4638788972655d”.

Version Policies

Microsoft .NET Framework supports version policies. These policies are stored in XML files and are simply a request to load one version of an assembly instead of another. This is exactly what we need here.

The policy for redirecting from old versions of Rebex FTP for .NET (2.0.0.0 - 3.0.3299.0) to 3.3300.0.0:

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">    
    <dependentAssembly>    
        <assemblyIdentity name="Rebex.Net.Ftp"
          publicKeyToken="1c4638788972655d" />    
        <bindingRedirect oldVersion="2.0.0.0-3.0.3299.0"
           newVersion="3.0.3300.0" />    
    </dependentAssembly>    
</assemblyBinding>

There are three levels at which version policy can be applied in .NET:

1. Application-Specific Policy

Each application has an optional configuration file that can specify the redirections. The name of the configuration file is the name of the executable + the ".config" extension (eg. WinFormClient.exe.config) for executable files and Web.config for ASP.NET applications.

To enforce binding to a different version than the application was linked with, place the version policy under the <runtime> node of the <configuration> root node:

<configuration>    
    <runtime>    
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">    
            <dependentAssembly>    
                <assemblyIdentity    
                    name="Rebex.Net.Ftp"    
                    publicKeyToken="1c4638788972655d" />    
                <bindingRedirect    
                    oldVersion="1.0.0.0-1.2.2.0"    
                    newVersion="1.3.0.0" />    
            </dependentAssembly>    
        </assemblyBinding>    
    </runtime>    
</configuration>

2. Global Assembly Cache Policy

This only works if the component is installed in Global Assembly Cache. The binding policy can be specified manually using .NET Configuration Management Console (see the screenshot) after adding the assemblies into "Configured Assemblies" group:

upgrade binding

One binding policy has to be defined for each Rebex assembly (only for the components you actually use and their references, of course). The drawback of this method is that this will affect all applications using Rebex components, so use with care.

3. Machine-Wide Policy.

Machine-wide policy is stored in machine.config file. Policy statements made in machine.config also affect all applications running on the machine. Edit this file only if you know what you are doing. See .NET Framework documentation for more information. We strongly suggest using one of the other methods instead.