I moved the code of my c++/cli (visual studio 2010) project into a namespace. Before that, everything was in the global namespace, but this has several drawbacks which needed to be addressed.
After the renaming, the binary deserialization of existing save files fail, with the following error message:
Serialization Exception occurred.
The object with ID 11 was referenced in a fixup but does not exist.
To address the renaming of the classes - which now reside in a namespace - I use a SerializationBinder.
ref class MySerializationBinder sealed: SerializationBinder
{
public:
virtual Type^ BindToType (String^ assemblyname, String^ typeName) override
{
Type^ result = Type::GetType (typeName);
if (result == nullptr)
{
result = Type::GetType ("MyNamespace." + typeName);
}
return result;
}
};
Does anyone have a clue of what might be going wrong here?
Translating the code from How to create a SerializationBinder for the Binary Formatter that handles the moving of types from one assembly and namespace to another into very quick n dirty c++ cli, we get something like
virtual Type^ BindToType (String^ assemblyName, String^ typeName) override
{
auto m = Regex::Match (typeName, "^(?<gen>[^\\[]+)\\[\\[(?<type>[^\\]]*)\\](,\\[(?<type>[^\\]]*)\\])*\\]$");
if (m->Success)
{
// generic type
Type^ gen = GetFlatTypeMapping (assemblyName, m->Groups["gen"]->Value);
List<Type^>^ genArgs = gcnew List<Type^> ();
for each(Capture^ c in Enumerable::Cast<Capture^> (m->Groups["type"]->Captures)){
Match^ m2 = Regex::Match (c->Value, "^(?<tname>.*)(?<aname>(,[^,]+){4})$");
String^ tname = m2->Groups["tname"]->Value->Trim ();
Type^ t = BindToType ("", tname);
genArgs->Add(t);
}
return gen->MakeGenericType (genArgs->ToArray ());
}
return GetFlatTypeMapping (assemblyName, typeName);
}
Type^ GetFlatTypeMapping (String^ assemblyName, String^ typeName)
{
Type^ result = Type::GetType (typeName);
if (result == nullptr)
{
result = Type::GetType ("MyNamespace." + typeName);
}
if (result == nullptr)
{
if (typeName->Contains ("BindingList"))
result = BindingList<int>::typeid->GetGenericTypeDefinition (); //int is just a placeholder
if (typeName->Contains ("KeyValuePair"))
result = KeyValuePair<int, int>::typeid->GetGenericTypeDefinition (); //int is just a placeholder
}
if (result == nullptr)
throw gcnew Exception ("Fail");
return result;
}
However, since I know which classes to translate, I found the following way easier. This way we do not have to care about nested generics either. The variable OldClassList
is assumed to be a list of strings containing the classnames that have to be moved into the namespace MyNamespace
.
virtual Type^ BindToType (String^ assemblyName, String^ typeName) override
{
String^ newTypeName = typeName;
for each(String^ name in OldClassList)
newTypeName = Regex::Replace (newTypeName, "(?<![.\\w])" + name + "\\b", "MyNamespace." + name);
newTypeName = Regex::Replace (newTypeName, "\\OldAssembly, Version", "NewAssembly, Version");
Type^ result = Type::GetType (newTypeName);
if (result == nullptr)
throw gcnew Exception ("Could not parse the string {0} to a valid type."->Format(typeName));
return result;
}