472,356 Members | 2,079 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,356 software developers and data experts.

Binary serialization + strongly named assemblies + GAC problem


I am having a problem deserializing objects from a library when the
following conditions exist:

1- The library is strongly named
2- The serialized file was created with version 1.0 of the assembly
3- I am trying to deserialize from an EXE that references version 2.0 of
the assembly
4- Both version 1.0 and 2.0 of the assembly reside in the GAC (no policy
redirects exist).

Note that this is not the AssemblyFormat = FormatterAssemblyStyle.Simple
problem. It is a different issue that arises because the old DLL can
still be loaded from the GAC.

object o = null;
Foo foo = null; // Foo is in the strong named assembly

BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
using(FileStream stream = File.OpenRead("foo.bin"))
{
o = formatter.Deserialize(stream);
}

foo = o as Foo;
// At this point, o is not null, but foo is null

Deserialization succeeds. The problem is that the deserialized object is
a version 1 object. During deserialization, .NET finds the old version
of the library in the GAC, even though I have compiled against version
2.0. The deserialized object is essentially unusable. The cast from
object to Foo returns a null object, since the returned object is not a
2.0 Foo. The modules view in the debugger shows that both versions of
the library are loaded.

If I remove the 1.0 version of the library from the GAC, deserialization
correctly returns a 2.0 Foo object. Is there a way to make this work
that doesn't involve removing the old library from the GAC? Having the
old version available solves a different set of problems for us.
Here is a complete set of steps + code to reproduce

1- Create a simple, serializable class

//---------- MyLib.cs-----------
using System;
using System.Collections.Generic;
using System.Reflection;

[assembly: AssemblyTitle("MyLib")]
[assembly: AssemblyVersion("1.0.0.0")]

namespace MyLib
{

[Serializable]
public class Foo
{
public Foo()
{
for(int i=0;i<10; ++i)
{
Values.Add(i);
}
}

public List<intValues = new List<int>();
}
}
2- Compile MyLib.cs into a strongly named library, and add it to the GAC:
sn -k key.snk
csc /t:library /keyfile:key.snk MyLib.cs
gacutil /i MyLib.dll

3- Create an app that serializes an instance of Foo from version 1.0 of
the library. Compile and execute

//-------Create.cs-------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

using MyLib;

static public class Program
{
[STAThread]
public static void Main()
{
Foo foo = new Foo();

BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;

using(FileStream stream = File.Create("foo.bin"))
{
formatter.Serialize(stream, foo);
}
}
}
csc /r:MyLib.dll Create.cs
Create.exe

4- Add a new field to Foo, bump the version, rebuild, and install the
new version into the GAC

//---------- MyLib.cs-----------
....<snip>
[assembly: AssemblyVersion("2.0.0.0")]

....<snip>

public class Foo
{
.... <snip>
public string Text = "hello";
}
csc /t:library /keyfile:key.snk MyLib.cs
gacutil /i MyLib.dll
5- Create an app that tries to load the file that was serialized from
version 1.0 of the DLL. Compile and execute

//-------Load.cs-------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

using MyLib;

static public class Program
{
[STAThread]
public static void Main()
{
object o = null;
Foo foo = null;

BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;

using(FileStream stream = File.OpenRead("foo.bin"))
{
o = formatter.Deserialize(stream);
}

Console.WriteLine(o.GetType().AssemblyQualifiedNam e);

foo = o as Foo;
if(foo == null)
Console.WriteLine("foo is null");
else
Console.WriteLine("foo is not null. Loaded correctly");
}
}
csc /r:MyLib.dll Load.cs
Load.exe
The output from Load.exe is

MyLib.Foo, MyLib, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=0b5ef2fdd6494a50

foo is null

If I remove the 1.0 version of MyLib from the GAC, deserialization
succeeds. The output is:

MyLib.Foo, MyLib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=0b5ef2fdd6494a50
foo is not null. Loaded correctly

H^2
Jul 24 '06 #1
5 5892
Harald,

Why not specify a redirection for your application? You can specify
that when the old version of the library is specified, you use the new
version.

If that doesn't work, I would set a reference to both dlls and then set
up aliases for them so that you can use both versions in your application.
Of course, this would require you to switch based on which version you are
working with, providing a shim to make it easier to use, most likely (so you
don't have to switch on every call).

Hope this helps.
--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"Harold Howe" <hh***********@mchsi.comwrote in message
news:eJ**************@TK2MSFTNGP04.phx.gbl...
>
I am having a problem deserializing objects from a library when the
following conditions exist:

1- The library is strongly named
2- The serialized file was created with version 1.0 of the assembly
3- I am trying to deserialize from an EXE that references version 2.0 of
the assembly
4- Both version 1.0 and 2.0 of the assembly reside in the GAC (no policy
redirects exist).

Note that this is not the AssemblyFormat = FormatterAssemblyStyle.Simple
problem. It is a different issue that arises because the old DLL can still
be loaded from the GAC.

object o = null;
Foo foo = null; // Foo is in the strong named assembly

BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
using(FileStream stream = File.OpenRead("foo.bin"))
{
o = formatter.Deserialize(stream);
}

foo = o as Foo;
// At this point, o is not null, but foo is null

Deserialization succeeds. The problem is that the deserialized object is a
version 1 object. During deserialization, .NET finds the old version of
the library in the GAC, even though I have compiled against version 2.0.
The deserialized object is essentially unusable. The cast from object to
Foo returns a null object, since the returned object is not a 2.0 Foo. The
modules view in the debugger shows that both versions of the library are
loaded.

If I remove the 1.0 version of the library from the GAC, deserialization
correctly returns a 2.0 Foo object. Is there a way to make this work that
doesn't involve removing the old library from the GAC? Having the old
version available solves a different set of problems for us.
Here is a complete set of steps + code to reproduce

1- Create a simple, serializable class

//---------- MyLib.cs-----------
using System;
using System.Collections.Generic;
using System.Reflection;

[assembly: AssemblyTitle("MyLib")]
[assembly: AssemblyVersion("1.0.0.0")]

namespace MyLib
{

[Serializable]
public class Foo
{
public Foo()
{
for(int i=0;i<10; ++i)
{
Values.Add(i);
}
}

public List<intValues = new List<int>();
}
}
2- Compile MyLib.cs into a strongly named library, and add it to the GAC:
sn -k key.snk
csc /t:library /keyfile:key.snk MyLib.cs
gacutil /i MyLib.dll


3- Create an app that serializes an instance of Foo from version 1.0 of
the library. Compile and execute

//-------Create.cs-------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

using MyLib;

static public class Program
{
[STAThread]
public static void Main()
{
Foo foo = new Foo();

BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;

using(FileStream stream = File.Create("foo.bin"))
{
formatter.Serialize(stream, foo);
}
}
}
csc /r:MyLib.dll Create.cs
Create.exe


4- Add a new field to Foo, bump the version, rebuild, and install the new
version into the GAC

//---------- MyLib.cs-----------
...<snip>
[assembly: AssemblyVersion("2.0.0.0")]

...<snip>

public class Foo
{
... <snip>
public string Text = "hello";
}
csc /t:library /keyfile:key.snk MyLib.cs
gacutil /i MyLib.dll

5- Create an app that tries to load the file that was serialized from
version 1.0 of the DLL. Compile and execute

//-------Load.cs-------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

using MyLib;

static public class Program
{
[STAThread]
public static void Main()
{
object o = null;
Foo foo = null;

BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;

using(FileStream stream = File.OpenRead("foo.bin"))
{
o = formatter.Deserialize(stream);
}

Console.WriteLine(o.GetType().AssemblyQualifiedNam e);

foo = o as Foo;
if(foo == null)
Console.WriteLine("foo is null");
else
Console.WriteLine("foo is not null. Loaded correctly");
}
}
csc /r:MyLib.dll Load.cs
Load.exe

The output from Load.exe is

MyLib.Foo, MyLib, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=0b5ef2fdd6494a50

foo is null

If I remove the 1.0 version of MyLib from the GAC, deserialization
succeeds. The output is:

MyLib.Foo, MyLib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=0b5ef2fdd6494a50
foo is not null. Loaded correctly

H^2


Jul 24 '06 #2
Harold Howe <hh***********@mchsi.comwrote:
>
I am having a problem deserializing objects from a library when the
following conditions exist:

1- The library is strongly named
2- The serialized file was created with version 1.0 of the assembly
3- I am trying to deserialize from an EXE that references version 2.0 of
the assembly
4- Both version 1.0 and 2.0 of the assembly reside in the GAC (no policy
redirects exist).
Yeah, it's a PITA isn't it? A real Microsoft Faux Paux if you ask me.

Try using this binder when you deserialize (attach it to the binary
formatter). You should be able to set the AssemblyFormat to Simple but that
doesn't seem to do it across assembly boundries.

sealed class SimpleDeserializationBinder : SerializationBinder
{
private Regex _assemRegex
= new Regex("(?<assembly>^.*?),.*");
private Regex _typeRegex
= new Regex("(?<type>.*?),(?<assembly>.*?),.*(?<end>]])");

public override Type BindToType(string assemblyName, string typeName)
{
// remove strong name from assembly
Match match = _assemRegex.Match(assemblyName);
if (match.Success)
{
assemblyName = match.Groups["assembly"].Value;
}

// remove strong name from any generic collections
match = _typeRegex.Match(typeName);
if (match.Success)
{
typeName = string.Format("{0},{1}{2}",
match.Groups["type"].Value,
match.Groups["assembly"].Value,
match.Groups["end"].Value);
}

// replace assembly name with the simple assembly
// name - strip the strong name off of the name
string type = string.Format("{0}, {1}", typeName,
assemblyName);

// The following line of code returns the type.
return Type.GetType(type);
}
}

--
Thomas T. Veldhouse
Key Fingerprint: 2DB9 813F F510 82C2 E1AE 34D0 D69D 1EDC D5EC AED1

Jul 24 '06 #3
Thomas T. Veldhouse <ve*****@yahoo.comwrote:
Try using this binder when you deserialize (attach it to the binary
formatter). You should be able to set the AssemblyFormat to Simple but that
doesn't seem to do it across assembly boundries.
BTW ... I utilize it as follows:

BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
binaryFormatter.Binder = new SimpleDeserializationBinder();

--
Thomas T. Veldhouse
Key Fingerprint: 2DB9 813F F510 82C2 E1AE 34D0 D69D 1EDC D5EC AED1

Jul 24 '06 #4
Why not specify a redirection for your application? You can specify
that when the old version of the library is specified, you use the new
version.
Thanks for the suggestion. Initially we couldn't use a redirect for
technical reasons. Our application is a shell that loads various
plugins. The plugins all run in the same app domain, which means they
share a common app configuration. But the plugins have varying DLL
dependendencies. For example, plugin A that relies on version 1 of
MyLib, and plugin B that relies on version 2. One set of redirects for
all plugins would not be adequate.

However, after further testing, a redirect seems to be the only reliable
solution to my serialization problem. It also solves a couple of other
problems that I didn't post, namely, the deserialization of generics
where the type parameter is a user defined type. So we are working on
redesigning our system so that each plugin runs in its own appdomain.
That is something we have wanted to do for a long time anyway, but never
got around to working out all the kinks. Now we have no choice.

H^2
Aug 2 '06 #5
Thomas T. Veldhouse wrote:
>
Yeah, it's a PITA isn't it? A real Microsoft Faux Paux if you ask me.

Try using this binder when you deserialize (attach it to the binary
formatter).
....

Thanks for the code. I tried using your binder. I was already using a
custom binder, so it was easy to incorporate your code into it.

At first, it seemed to work just fine. Then in started failing. What I
discovered is that calling

Type.GetType("MyLib.Foo, MyLib");

succeeds when MyLib.dll is in the same directory as the executable. It
doesn't matter that MyLib is strongly named and was already loaded from
the GAC. It isn't loaded again, and type resolution succeeds.

However, if I nuke the local DLL, then type resolution fails, and I am
back to square one. I also experimented with just nuking the version
information from the fully qualified assembly name. This resulted in the
same behavior.

H^2
Aug 2 '06 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Kyle Blaney | last post by:
What happens when a strongly-named assembly is not in the global assembly cache (GAC) but is installed in two application's bin directories (with each copy being identical in every way - the exact...
1
by: Jack Menendez | last post by:
I have a forms based plugin architecture using C# that includes help files using MSHelpServices. However, my strongly named assemblies cannot be created because Interop.MSHelpServices is not strongly...
0
by: Chris Dunaway | last post by:
I'm looking for best practices for strongly naming assemblies in a team environment. We have a number of developers in our team and as often is the case, it will be necessary to share common...
4
by: Tamir Khason | last post by:
I have a form. On form there is my control (all of control's assemblies signed by strong key), BUT while running I recieve he located assembly 'MyFooAssembly' is not strongly named. While...
1
by: San | last post by:
Hi, Why strongly named assembly can refer other strongly named assembly ? Thanks with Regards, San.
7
by: schoenfeld1 | last post by:
I've implemented IPC between two applications using named pipes and binary serialization, but have noticed that the binary formatter is rather slow. It seems that the binary formatter reflects...
0
by: john | last post by:
The changes to asp.net makes it very difficult for us to migrate one of our web projects to 2.0 and makes deployments more difficult for us. It seems that the new Asp.net model is only designed...
0
by: inpuarg | last post by:
I have more then 5 projects in one solution. (c# VS 2005) GUI , BOL , DAL , Common Library , Extended Grid Component etc. All are compiled as strongly named assemblies. I am using public...
1
by: kikisan | last post by:
I am developing a windows service which utilizes the following classes: interface IPersistable; abstract class PersistableObject : IPersistable;
2
by: Kemmylinns12 | last post by:
Blockchain technology has emerged as a transformative force in the business world, offering unprecedented opportunities for innovation and efficiency. While initially associated with cryptocurrencies...
0
by: Naresh1 | last post by:
What is WebLogic Admin Training? WebLogic Admin Training is a specialized program designed to equip individuals with the skills and knowledge required to effectively administer and manage Oracle...
0
by: antdb | last post by:
Ⅰ. Advantage of AntDB: hyper-convergence + streaming processing engine In the overall architecture, a new "hyper-convergence" concept was proposed, which integrated multiple engines and...
1
by: Matthew3360 | last post by:
Hi there. I have been struggling to find out how to use a variable as my location in my header redirect function. Here is my code. header("Location:".$urlback); Is this the right layout the...
2
by: Matthew3360 | last post by:
Hi, I have a python app that i want to be able to get variables from a php page on my webserver. My python app is on my computer. How would I make it so the python app could use a http request to get...
0
hi
by: WisdomUfot | last post by:
It's an interesting question you've got about how Gmail hides the HTTP referrer when a link in an email is clicked. While I don't have the specific technical details, Gmail likely implements measures...
1
by: Matthew3360 | last post by:
Hi, I have been trying to connect to a local host using php curl. But I am finding it hard to do this. I am doing the curl get request from my web server and have made sure to enable curl. I get a...
0
Oralloy
by: Oralloy | last post by:
Hello Folks, I am trying to hook up a CPU which I designed using SystemC to I/O pins on an FPGA. My problem (spelled failure) is with the synthesis of my design into a bitstream, not the C++...
0
by: Ricardo de Mila | last post by:
Dear people, good afternoon... I have a form in msAccess with lots of controls and a specific routine must be triggered if the mouse_down event happens in any control. Than I need to discover what...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.