Sérialisation des types Mixte (C++/CLI)
On m’a souvent posé la question de la sérialisation des types mixte C++/CLI.
En effet la capacité d’embarquer des pointeurs natif dans des ref class pose un problème quand la class est marquée [Serializable].
La solution pour maitriser comment et quelle donnée va être sérialisé est d’implémenter l’interface ISerializable.
Cette interface implique d’avoir un constructeur par défaut, un constructeur surchargé prenant en paramètre (SerializationInfo ^si, StreamingContext sc) et surcharger la méthode virtuelle : virtual void GetObjectData(SerializationInfo ^si, StreamingContext sc).
Dans cette dernière méthode et le constructeur surchargé, on doit « manuellement » préciser les champs a sérialiser via :
si->AddValue("m_nCount",m_nCount);
et accéder aux données via : m_nCount = si->GetInt32("m_nCount");
Prenons un exemple :
[Serializable]
public ref class MyClass2 : public ISerializable
{
public :
int m_nCount;
int m_nData;
Native* pn;
virtual void GetObjectData(SerializationInfo ^si, StreamingContext sc)
{
si->AddValue("m_nCount",m_nCount);
si->AddValue("m_nData",m_nData);
si->AddValue("pnx",pn->x);
si->AddValue("pny",pn->y);
String^ s = gcnew String(pn->chaine);
si->AddValue("chaine",s);
}
MyClass2()
{
m_nCount = -1;
m_nData = -1;
pn=new Native(8,2);
}
protected:
MyClass2(SerializationInfo ^si, StreamingContext sc)
{
if (pn==nullptr)
pn=new Native(8,2);
int i =si->GetInt32("m_nCount");
m_nCount = i;
m_nData = si->GetInt32("m_nData");
pn->x = si->GetInt32("pnx");
pn->y = si->GetInt32("pny");
String ^tt= si->GetString("chaine");
StringUtilities::StringConvertor scc(tt);
strcpy_s(pn->chaine,scc.NativeCharPtr);
}
~MyClass2()
{
delete pn;
}
!MyClass2()
{
delete pn;
}
};
Vous noterez que la ref class embarque un pointeur sur une classe native (code mixte)que j’alloue sur le tas natif au moment de la construction et détruit au moment de la destruction et finalisation de la ref class.
Pour faciliter le transtypage des chaines natives vers les System ::String (et en attendant la marshall library prévue pour Orcas) l’excellent code source de Nishant Sivakumar (blog.voidnish.com) publié sur https://www.codeproject.com/managedcpp/StringConvertor.asp Celui ci auteur d’un ouvrage important sur le C++/CLI, propose une classe de StringConertor (tout est dans nom !)..y’a plus qu’à l’utiliser !
Pour corser l’affaire j’au utilisé un buffer de char dans la classe native, le stringconvertor gère pour moi les copies entre tas natif et tas managé :
property char* NativeCharPtr
{
char* get()
{
IntPtr ptr = Marshal::StringToHGlobalAnsi(m_String);
if(ptr != IntPtr::Zero)
{
m_vec_hglobal->push_back(ptr);
return reinterpret_cast<char*>(static_cast<void*>(ptr));
}
else
return nullptr;
}
}
Reste à utiliser cette classe avec un serialisateur : (ici en binaire, mais on pourrait utiliser le soapformater…ou le xmlformater de WCF)
FileStream ^fs = gcnew FileStream("data2.txt" ,
FileMode::Create, FileAccess::ReadWrite);
BinaryFormatter ^bf = gcnew BinaryFormatter();
// SoapFormatter ^bf = gcnew SoapFormatter();
bf->Serialize(fs,mc);
fs->Close();
La dé sérialisation s’opère de manière symétrique :
MyClass2 ^data;
FileStream fs ("data2.txt" , FileMode::Open, FileAccess::ReadWrite);
BinaryFormatter bf;
data = (MyClass2^) bf.Deserialize(%fs);
Console::WriteLine("data->Count is {0}",
data->m_nCount.ToString());
Console::WriteLine("data->Data is {0}",
data->m_nData.ToString());
Console::WriteLine("data->pn->x is {0}",
data->pn->x.ToString());
Console::WriteLine("data->pn->y is {0}",
data->pn->y.ToString());
StringUtilities::StringConvertor sc(data->pn->chaine);
Console::WriteLine("data->pn->chaine is {0}",
sc.ToString());
fs.Close();
En synthèse il est facile de rendre une ref class de type mixte sérialisable et via l’implémentation de ISerializable de maitriser ce qui doit être persisté.
A bientôt,
Eric
Comments
- Anonymous
January 08, 2008
J'ai réalisé ce type d'objet (qui foncitonne très bien) mais je voudrais maintenant le passer par .Net Remoting dans le contexte suivant: Un client C# appelle une méthode serveur d'un objet C# qui retourne un objet C++/CLI intégrant un objet natif ... afin de pouvoir réutiliser dans une nouvelle application C# du code C++/MFC existant et opérationnel. J'ai ajouté une interface à l'objet C++/CLI, je passe bien dans le GetObjectData coté serveur mais je prends une exception coté client (je ne passe pas par le constructeur permettant de désérialiser). Merci d'avance.