Oggi mi sono trovato di fronte ad una strana situazione che mi ha impegnato l’intera mattinata per venirne a capo (sì sto lavorando, devo recuperare…): binding di oggetti COM in WPF.
Antefatto: è un mesetto che sto prendendo WPF e Silverlight per le corna, due tecnologie che in passato non avevo modo di utilizzare e che invece da un mese a questa parte sono entrate di prepotenza nel mio panorama professionale.
Sto procedendo alla personalizzazione di un applicativo di terze parti che fa uso pesantemente di COM per quanto riguarda i mattoncini di cui è composto e che vengono utilizzati anche per la sua customizzazione mediante un vasto SDK (si tratta di un applicativo GIS desktop: ESRI ArcMap).
l’SDK in questione è stato totalmente wrappato per l’utilizzo con .NET mediante la metodologia standard dei Remote Callable Wrapper (RCW), questi non sono altro che classi proxy .NET che effettuano il marshaling delle chiamate a metodi e proprietà da e per l’oggetto COM vero e proprio.
In generale se la classe COM si chiama MyClass, il processo di importazione (che si esegue mediante l’utility tlbimp.exe) crea lato .NET un’interfaccia denominata MyClass e una classe RCW denominata MyClassClass.
Il problema nasce quando è l’infrastruttura dell’applicativo che fornisce un oggetto a seguito di una chiamata all’SDK: in generale accade che l’oggetto ritornato non è di tipo MyClassClass bensì di tipo System.__ComObject. Questo è un tipo interno al framework (è marcato internal) ed in sostanza è il contenitore generico per ogni oggetto COM wrappato, in pratica si perde la possibilità di castarlo a MyClassClass nonostante l’oggetto di per se in effetti lo sia.
Che cosa comporta tutto questo? Ad esempio che se siamo in presenza di binding WPF mediante path che punta ad una proprietà di un oggetto RCW, questo non funziona perché l’infrastruttura di WPF non è in grado di risolvere la chiamata alla proprietà indicata a partire da un oggetto System.__ComObject.
Nota a margine: da quello che ho letto durante le mie ricerche su Google questo comportamento lo si ha anche nell’interoperabilità con Office, tanto che la soluzione al problema mi è stata suggerita in un post di un forum MSDN che trattava il binding all’interno di un’applicazione WPF, la quale andava a filtrare gli appuntamenti archiviati con Outlook.
La soluzione a questo problema è quella di utilizzare il metodo statico CreateWrapperOfType della classe System.Runtime.InteropServices.Marshal, questo metodo serve in pratica a trasformare un oggetto RCW in un altro oggetto RCW di tipo diverso. In sostanza è proprio quello che ci serve per trasformare (castare non sarebbe il termine adatto in senso stretto) un oggetto System.__ComObject in quello che sarebbe il suo vero tipo RCW:
//myGenericComObj è di tipo System.__ComObject
MyClassClass rcwObj = Marshal.CreateWrapperOfType(myGenericComObj, typeof(MyClassClass)) as MyClassClass;
Nota importante: questo metodo va utilizzato con il tipo della classe RCW e non con il tipo dell’interfaccia, ovvero non funziona se al posto di MyClassClass si specificasse l’interfaccia MyClass (ricordo che entrambe sono il risultato della procedura di importazione di un oggetto COM).
A questo punto, con l’oggetto RCW non più generico, il binding WPF funzionerà correttamente riuscendo a risolvere normalmente i vari riferimenti alle proprietà che identificano i path.