473,839 Members | 1,402 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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 = FormatterAssemb lyStyle.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.Assem blyFormat = FormatterAssemb lyStyle.Simple;
using(FileStrea m stream = File.OpenRead(" foo.bin"))
{
o = formatter.Deser ialize(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.Collecti ons.Generic;
using System.Reflecti on;

[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.Collecti ons.Generic;
using System.IO;
using System.Runtime. Serialization;
using System.Runtime. Serialization.F ormatters;
using System.Runtime. Serialization.F ormatters.Binar y;

using MyLib;

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

BinaryFormatter formatter = new BinaryFormatter ();
formatter.Assem blyFormat = FormatterAssemb lyStyle.Simple;

using(FileStrea m stream = File.Create("fo o.bin"))
{
formatter.Seria lize(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.Collecti ons.Generic;
using System.IO;
using System.Runtime. Serialization;
using System.Runtime. Serialization.F ormatters;
using System.Runtime. Serialization.F ormatters.Binar y;

using MyLib;

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

BinaryFormatter formatter = new BinaryFormatter ();
formatter.Assem blyFormat = FormatterAssemb lyStyle.Simple;

using(FileStrea m stream = File.OpenRead(" foo.bin"))
{
o = formatter.Deser ialize(stream);
}

Console.WriteLi ne(o.GetType(). AssemblyQualifi edName);

foo = o as Foo;
if(foo == null)
Console.WriteLi ne("foo is null");
else
Console.WriteLi ne("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= 0b5ef2fdd6494a5 0

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= 0b5ef2fdd6494a5 0
foo is not null. Loaded correctly

H^2
Jul 24 '06 #1
5 6034
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.co m

"Harold Howe" <hh***********@ mchsi.comwrote in message
news:eJ******** ******@TK2MSFTN GP04.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 = FormatterAssemb lyStyle.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.Assem blyFormat = FormatterAssemb lyStyle.Simple;
using(FileStrea m stream = File.OpenRead(" foo.bin"))
{
o = formatter.Deser ialize(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.Collecti ons.Generic;
using System.Reflecti on;

[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.Collecti ons.Generic;
using System.IO;
using System.Runtime. Serialization;
using System.Runtime. Serialization.F ormatters;
using System.Runtime. Serialization.F ormatters.Binar y;

using MyLib;

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

BinaryFormatter formatter = new BinaryFormatter ();
formatter.Assem blyFormat = FormatterAssemb lyStyle.Simple;

using(FileStrea m stream = File.Create("fo o.bin"))
{
formatter.Seria lize(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.Collecti ons.Generic;
using System.IO;
using System.Runtime. Serialization;
using System.Runtime. Serialization.F ormatters;
using System.Runtime. Serialization.F ormatters.Binar y;

using MyLib;

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

BinaryFormatter formatter = new BinaryFormatter ();
formatter.Assem blyFormat = FormatterAssemb lyStyle.Simple;

using(FileStrea m stream = File.OpenRead(" foo.bin"))
{
o = formatter.Deser ialize(stream);
}

Console.WriteLi ne(o.GetType(). AssemblyQualifi edName);

foo = o as Foo;
if(foo == null)
Console.WriteLi ne("foo is null");
else
Console.WriteLi ne("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= 0b5ef2fdd6494a5 0

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= 0b5ef2fdd6494a5 0
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 SimpleDeseriali zationBinder : SerializationBi nder
{
private Regex _assemRegex
= new Regex("(?<assem bly>^.*?),.*");
private Regex _typeRegex
= new Regex("(?<type> .*?),(?<assembl y>.*?),.*(?<end >]])");

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

// remove strong name from any generic collections
match = _typeRegex.Matc h(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(ty pe);
}
}

--
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 = FormatterAssemb lyStyle.Simple;
binaryFormatter .Binder = new SimpleDeseriali zationBinder();

--
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("M yLib.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
1907
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 same version, public key, culture information, etc.)? I understand the benefits of deploying shared assemblies with each application instead of using the GAC but is increased memory usage a disadvantage of this approach? For example, suppose...
1
2314
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 named. I get the following message during a build, presumedly from AL.exe: Assembly generation failed -- Referenced assembly 'Interop.MSHelpServices' does not have a strong name What is the strategy for handling this? -- Jack Menendez...
0
886
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 code assemblies. We also want to strong name our assemblies. How should the keys be generated, using SN.exe? Should the resulting output file be shared? Stored somewhere? Should all of our assemblies be strongly named with the same key pair?
4
1678
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 looking into References node in project the assembly 'MyFooAssembly' is strong key = true. So what cna be a ptoblem??? TNX
1
1403
by: San | last post by:
Hi, Why strongly named assembly can refer other strongly named assembly ? Thanks with Regards, San.
7
9562
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 the entire type everytime it is invoked to serialize/deserialize an object of that type. Is there a way to prepare the binary formatter with a pre-defined type, such that it only reflects once but can be re-used to serialize/deserialize objects...
0
1573
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 to place assemblies in the bin directory of the application. For our application, we need to deploy to the GAC. We have customized a SharePoint portal and have put in our own pages and controls (both user and custom) into SharePoint pages. ...
0
1744
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 key(generating this key from public/private pair, securing private key to later usage at release) and delay signing option. Public methods declared as internal and in AssemblyInfo.cs file of each project there are
1
11114
by: kikisan | last post by:
I am developing a windows service which utilizes the following classes: interface IPersistable; abstract class PersistableObject : IPersistable;
0
9856
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
10911
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10589
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
10298
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9429
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5683
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5867
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
4066
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
3136
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.