2011-07-27 1 views
0

J'ai un addin automation (implémentant Extensibility.IDTExtensibility2), écrit en C# 4, dans lequel j'essaye de charger des données sérialisées (binaires). Il fonctionne parfaitement dans les tests unitaires, mais échoue lors de l'exécution à partir d'Excel:La désérialisation C# échoue dans Automation Addin mais pas dans les tests NUnit

Unable to find assembly 'XXX, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. 
    at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly() 
    at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable) 
    at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record) 
    at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum) 
    at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run() 
    at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream) 
    at 

Sur BinaryFormatter je réglage de la AssemblyFormat (pour les deux sérialisation et désérialisation) comme suit:

serializationCodec.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple; 

Ce que je pensé ignorerait la version selon here. Ensuite, je pensais que cela pouvait être dû à la notion de "Trusted Location" d'Excel, j'ai donc ajouté le répertoire du projet et vérifié tous les sous-répertoires, mais l'erreur est restée.

En vain, j'ai essayé d'ajouter l'attribut System.Runtime.Serialization.OptionalFieldAttribute, mais cela n'a pas aidé. Les tests unitaires peuvent charger les fichiers sérialisés générés par lui-même ou le même code exécuté dans Excel, mais Excel ne peut pas charger les données sérialisées, qu'il ait ou non effectué la sérialisation réelle. Le fait qu'Excel ne puisse pas désérialiser ce qu'il a lui-même sérialisé indique qu'il s'agit d'un redherring car il aurait évidemment accès à l'assemblage utilisé.

Donc la question est pourquoi Excel désérialiser différemment par rapport à mon test unitaire? (Ou peut-être plus important encore, comment est-ce que je peux obtenir la désérialisation fonctionnant dans Excel?)

merci.

+0

MISE À JOUR: Retrait du paramètre AssemblyFormat ne remédie pas à la situation.La seule raison pour laquelle cela est présent est de faciliter les tests unitaires (je charge juste les données sérialisées précédentes) – earcam

+0

Je suis assez certain que cela est dû au [ClassInterface (ClassInterfaceType.AutoDual)] qui produit un avertissement "... utilise ClassInterface (ClassInterfaceType .AutoDual) qui ne peut pas version correctement ... "code: CA1408. Donc maintenant je dois trouver un moyen de supprimer cette ClassInterface qui permettra à un addin d'automatisation d'enregistrer des UDF et d'implémenter IDTExtensibility2 pour obtenir un handle sur Excel.Application – earcam

Répondre

0

J'ai chassé les harengs rouges et je n'ai obtenu nulle part avec l'hypothèse qu'il s'agissait d'un problème de versioning (j'avais regardé des shims et toutes sortes).

Il était en fait dû à une liaison d'assemblage bizarre; Bien que la DLL d'automation d'Addin puisse voir et charger des classes à partir des DLL dont elle dépendait, elle ne parvient pas à trouver les assemblages nécessaires, même si l'appel de désérialisation provient d'une classe appartenant à une DLL. trouver!

solution est certainement un hack mais il m'a passé cela, donc au cas où quelqu'un d'autre est coincé comme je l'étais, voici le hack (s):

intérieur d'une classe X, qui dépend des classes Y et Z chacun de leurs DLLs de projet respectifs (nommés ProjectForX, ProjectForY et ProjectForZ)

private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args) 
    { 
     if(args.Name.StartsWith("ProjectForX,")) { 
      return typeof(X).Assembly; 
     } else if(args.Name.StartsWith("ProjectForY,")) { 
      return typeof(Y).Assembly; 
     } else if(args.Name.StartsWith("ProjectForZ,")) { 
      return typeof(Z).Assembly; 
     } 
     return null; 
    } 


    public static X LoadX(string filename) 
    { 
     AppDomain currentDomain = AppDomain.CurrentDomain; 
     ResolveEventHandler handler = new ResolveEventHandler(MyResolveEventHandler); 
     currentDomain.AssemblyResolve += handler; 
     try { 
      Stream stream = new FileStream(@filename, FileMode.Open); 
      try { 
       BinaryFormatter deserializer = createBinaryFormatter(); 
       X model = (X)deserializer.Deserialize(stream); 
       return model; 
      } finally { 
       stream.Close(); 
      } 
     } finally { 
      currentDomain.AssemblyResolve -= handler; 
     } 
    } 

Fondamentalement, il accroche juste dans les événements de détermination et fournit l'assemblage correct en fonction du nom du type demandé - ce n'est pas particulièrement sûr (I » m en utilisant la virgule dans le descripteur d'assembly pour essayer de minimiser les conflits).

Il y a certainement une façon claire de le faire, et le problème est probablement dû à une mauvaise configuration et à mon expérience limitée avec les produits MS et C# en particulier.

Cela n'aurait pas été un problème si je venais d'une DLL monolithique construit =)

Questions connexes