Can't get .NET Policy DLL to redirect application to newer assembly

2.3k views Asked by At

My background in technical support had likely resulted in me putting far too much information in this post, leading to nobody being interested in reading it, but IMHO, more information is better than less.

There is something very basic and fundamental about assembly binding and redirection that I'm missing. I'm trying to create a policy DLL to redirect applications that have compiled against an older version of my assembly to the newest version. Nothing I do seems to work. I've outlined the steps I take below. I'm not using Visual Studio, but just the command line tools included with the .NET Framework and the Windows SDK. Can anybody tell me why, in step eleven, when I run the application that was compiled against the v1.0.0 version of my assembly, that the application is not redirected towards the v2.0.0 assembly that is registered in the GAC?

As you follow along what I did, you'll often have to take note of the directory I've executed commands in.

I would be more than happy with an RTFM answer, if you'd just be so kind as to point to TFM I'm supposed to read.

Thanks in advance, Reg

Step One : Create a key pair to strongly name my assembly.

c:\stackexchangetest> sn -k stackTest.snk
Microsoft (R) .NET Framework Strong Name Utility  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Key pair written to stackTest.snk

c:\stackexchangetest> sn -p stackTest.snk publicStackTest.snk
Microsoft (R) .NET Framework Strong Name Utility  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key written to publicStackTest.snk

c:\stackexchangetest> sn -tp publicStackTest.snk
Microsoft (R) .NET Framework Strong Name Utility  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key is
002400000480000094000000060200000024000052534131000400000100010079ba0ab991ceda
d7ba4a767d55889a6cd0eafc9bc15c394620b61c02f4c1112dd9e348cbb4826775c72164ac8552
f9b751b1a1dd71874c04b24221f00f4ee8c2971f3ac8b6b381b26d39cbd3347e773bf08e9af838
6d7f317592951a4985e2c187288d037d5af2f62cce2e4dc700d0d00feb4c095df5d12c9a0908dc
8606cdad

Public key token is 6dfd37060fccec62

Step Two : Create an assembly whose version is v1.0.0

c:\stackexchangetest\1.0.0> type StackTest.cs
using System;

namespace stack.exchange.rdomarat
{
    public class stackTest
    {
        public static String getVersion()
        {
            return( "1.0.0" );
        }
    }
}

c:\stackexchangetest\1.0.0> type AssemblyInfo.cs
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("stack.exchange.rdomarat")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("StackExchange")]
[assembly: AssemblyProduct("rdomarat")]
[assembly: AssemblyCopyright("Copyright 2012")]
[assembly: AssemblyTrademark("rdomarat")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("a3d17815-97f5-4e56-803c-06f652a23369")]
[assembly: AssemblyVersion( "1.0.0" )]

c:\stackexchangetest\1.0.0> csc /out:stack.exchange.rdomarat.dll /keyfile:../stackTest.snk /debug /w:4 /t:library AssemblyInfo.cs StackTest.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.5420
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

Step Three : Create a policy DLL for the v1.0.0 assembly

c:\stackexchangetest\1.0.0> type stack.exchange.rdomarat.config
<configuration>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="stack.exchange.rdomarat"
                        publicKeyToken="6dfd37060fccec62"
                        culture=neutral
                    />
                <bindingRedirect oldVersion="1.0.0-1.0.0"
                        newVersion="1.0.0"
                    />
            </dependentAssembly>
        </assemblyBinding>
    </runtime> 
</configuration>

c:\stackexchangetest\1.0.0> al.exe /version:1.0.0 /link:stack.exchange.rdomarat.config /out:policy.1.0.stack.exchange.rdomarat.dll /keyfile:../stackTest.snk /description:"Policy file" /title:"Policy file" /company:"StackExchange" /copyright:"Copyright 2012" /trademark:"rdomarat" /product:"rdomarat"
Microsoft (R) Assembly Linker version 9.0.30729.1
Copyright (C) Microsoft Corporation. All rights reserved.

Step Four : Register the two assemblies into the GAC

c:\stackexchangetest\1.0.0> gacutil -u stack.exchange.rdomarat
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

No assemblies found matching: stack.exchange.rdomarat
Number of assemblies uninstalled = 0
Number of failures = 0

c:\stackexchangetest\1.0.0> gacutil -u policy.1.0.stack.exchange.rdomarat
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

No assemblies found matching: policy.1.0.stack.exchange.rdomarat
Number of assemblies uninstalled = 0
Number of failures = 0

c:\stackexchangetest\1.0.0> gacutil -i stack.exchange.rdomarat.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

c:\stackexchangetest\1.0.0> gacutil -i policy.1.0.stack.exchange.rdomarat.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

Step Five : Create an application that uses the new v1.0.0 assembly

c:\stackexchangetest\stackapp\1.0.0> type stackApp.cs
using System;
using stack.exchange.rdomarat;

public class StackApp
{
   public static void Main()
   {
       String s1;
       s1 = stackTest.getVersion();
       Console.WriteLine("Hello " + s1);
   }
}

c:\stackexchangetest\stackapp\1.0.0> csc /out:StackApp.exe /target:exe /reference:..\..\1.0.0\stack.exchange.rdomarat.dll StackApp.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.5420
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

c:\stackexchangetest\stackapp\1.0.0> stackapp
Hello 1.0.0

Step Six : Create a v2.0.0 stack.exchange.rdomarat.dll assembly

c:\stackexchangetest\2.0.0> type StackTest.cs
using System;

namespace stack.exchange.rdomarat
{
    public class stackTest
    {
        public static String getVersion()
        {
            return( "2.0.0" );
        }
    }
}

c:\stackexchangetest\2.0.0> type AssemblyInfo.cs
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("stack.exchange.rdomarat")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("StackExchange")]
[assembly: AssemblyProduct("rdomarat")]
[assembly: AssemblyCopyright("Copyright 2012")]
[assembly: AssemblyTrademark("rdomarat")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("a3d17815-97f5-4e56-803c-06f652a23369")]
[assembly: AssemblyVersion( "2.0.0" )]

c:\stackexchangetest\2.0.0> csc /out:stack.exchange.rdomarat.dll /keyfile:../stackTest.snk /debug /w:4 /t:library AssemblyInfo.cs StackTest.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.5420
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

Step Seven : Create a policy DLL for the v2.0.0 assembly

c:\stackexchangetest\2.0.0> type stack.exchange.rdomarat.config
<configuration>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="stack.exchange.rdomarat"
                        publicKeyToken="6dfd37060fccec62"
                        culture=neutral
                    />
                <bindingRedirect oldVersion="1.0.0-2.0.0"
                        newVersion="2.0.0"
                    />
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>

c:\stackexchangetest\2.0.0> al.exe /version:2.0.0 /link:stack.exchange.rdomarat.config /out:policy.2.0.stack.exchange.rdomarat.dll /keyfile:../stackTest.snk /description:"Policy file" /title:"Policy file" /company:"StackExchange" /copyright:"Copyright 2012" /trademark:"rdomarat" /product:"rdomarat"
Microsoft (R) Assembly Linker version 9.0.30729.1
Copyright (C) Microsoft Corporation. All rights reserved.

Step Eight : Register the two v2.0.0 assemblies into the GAC

c:\stackexchangetest\2.0.0> gacutil -u policy.2.0.stack.exchange.rdomarat
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

No assemblies found matching: policy.2.0.stack.exchange.rdomarat
Number of assemblies uninstalled = 0
Number of failures = 0

c:\stackexchangetest\2.0.0> gacutil -i stack.exchange.rdomarat.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

c:\stackexchangetest\2.0.0> gacutil -i policy.2.0.stack.exchange.rdomarat.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

Step Nine : Create an application that uses the new v2.0.0 assembly

c:\stackexchangetest\stackapp\2.0.0> type stackApp.cs
using System;
using stack.exchange.rdomarat;

public class StackApp
{
   public static void Main()
   {
       String s1;
       s1 = stackTest.getVersion();
       Console.WriteLine("Hello " + s1);
   }
}

c:\stackexchangetest\stackapp\2.0.0> csc /out:StackApp.exe /target:exe /reference:..\..\2.0.0\stack.exchange.rdomarat.dll StackApp.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.5420
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

c:\stackexchangetest\stackapp\2.0.0> StackApp.exe
Hello 2.0.0

Step Ten : Unregister everything from the GAC, and confirm that neither app works

c:\stackexchangetest\stackapp\2.0.0> gacutil -u policy.2.0.stack.exchange.rdomarat
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly: policy.2.0.stack.exchange.rdomarat, Version=2.0.0.0, Culture=neutral,PublicKeyToken=6dfd37060fccec62, processorArchitecture=MSIL
Uninstalled: policy.2.0.stack.exchange.rdomarat, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62, processorArchitecture=MSIL
Number of assemblies uninstalled = 1
Number of failures = 0

c:\stackexchangetest\stackapp\2.0.0> gacutil -u policy.1.0.stack.exchange.rdomarat
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly: policy.1.0.stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral,PublicKeyToken=6dfd37060fccec62, processorArchitecture=MSIL
Uninstalled: policy.1.0.stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62, processorArchitecture=MSIL
Number of assemblies uninstalled = 1
Number of failures = 0

c:\stackexchangetest\stackapp\2.0.0> gacutil -u stack.exchange.rdomarat
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly: stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62, processorArchitecture=MSIL
Uninstalled: stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62, processorArchitecture=MSIL

Assembly: stack.exchange.rdomarat, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62, processorArchitecture=MSIL
Uninstalled: stack.exchange.rdomarat, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62, processorArchitecture=MSIL
Number of assemblies uninstalled = 2
Number of failures = 0

c:\stackexchangetest\stackapp\1.0.0> StackApp.exe
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62' or one of its dependencies. The system cannot find the file specified.
File name: 'stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62' at StackApp.Main()

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorwks.dll
Running under executable  c:\StackExchangeTest\StackApp\1.0.0\StackApp.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = SYBASE\rdomarat
LOG: DisplayName = stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62 (Fully-specified)
LOG: Appbase = file:///C:/Users/rdomarat/Documents/Issues/111205_mlclient_policy/StackExchangeTest/StackApp/1.0.0/
LOG: Initial PrivatePath = NULL 
Calling assembly : StackApp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
LOG: Post-policy reference: stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62
LOG: The same bind was seen before, and was failed with hr = 0x80070002.

c:\stackexchangetest\stackapp\2.0.0> StackApp.exe
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'stack.exchange.rdomarat, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62' or one of its dependencies. The system cannot find the file specified.
File name: 'stack.exchange.rdomarat, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62' at StackApp.Main()

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorwks.dll
Running under executable  c:\StackExchangeTest\StackApp\2.0.0\StackApp.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = SYBASE\rdomarat
LOG: DisplayName = stack.exchange.rdomarat, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62 (Fully-specified)
LOG: Appbase = file:///C:/Users/rdomarat/Documents/Issues/111205_mlclient_policy/StackExchangeTest/StackApp/2.0.0/
LOG: Initial PrivatePath = NULL
Calling assembly : StackApp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
LOG: Post-policy reference: stack.exchange.rdomarat, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62
LOG: The same bind was seen before, and was failed with hr = 0x80070002.

Step Eleven : Re-register the v2.0.0 assembly, both application should now work

c:\stackexchangetest\2.0.0> gacutil -i stack.exchange.rdomarat.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

c:\stackexchangetest\2.0.0> gacutil -i policy.2.0.stack.exchange.rdomarat.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

c:\stackexchangetest\stackapp\2.0.0> StackApp.exe
Hello 2.0.0

c:\stackexchangetest\stackapp\1.0.0> StackApp.exe
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62' or one of its dependencies. The system cannot find the file specified.
File name: 'stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62' at StackApp.Main()

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorwks.dll
Running under executable  c:\StackExchangeTest\StackApp\1.0.0\StackApp.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = SYBASE\rdomarat
LOG: DisplayName = stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62 (Fully-specified)
LOG: Appbase = file:///C:/Users/rdomarat/Documents/Issues/111205_mlclient_policy/StackExchangeTest/StackApp/1.0.0/
LOG: Initial PrivatePath = NULL
Calling assembly : StackApp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
LOG: Post-policy reference: stack.exchange.rdomarat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dfd37060fccec62
LOG: The same bind was seen before, and was failed with hr = 0x80070002.
2

There are 2 answers

0
Reg Domaratzki On BEST ANSWER

Turns out my whole problem from day one was the lack of double quotes around :

culture=neutral

I change the .config file to read

culture="neutral"

and everything worked like a charm. An error in the assembly binding debug outlog log that indicated a parse error in the XML for the config file would have been handy, and might have saved me two days. Oh well.

8
J... On

I think this question could have been a lot shorter.

When compiling against a strongly named assembly you require the exact version of the assembly to be registered in the GAC. The app compiled against the V1.0.0.0 assembly requires assembly V1.0.0.0 to be registered/installed. The app compiled against the V2.0.0.0 assembly requires V2.0.0.0 to be registered/installed. If you want to run both apps then you need both versions installed and registered - no exceptions or substitutions with strongly named assemblies, as far as I am aware.

The whole point of this is that code written for one version can continue to run without fear of breaking if the assembly version is updated at some point in the future (ie : dll hell) - it will continue to run with the older version of the assembly until the developer gets around to updating the base application to run against the newer version.

On redirecting Assemblies :

http://msdn.microsoft.com/en-us/library/7wd6ex19%28v=vs.85%29.aspx