C# 反序列化在 Automation Addin 中失败,但在 NUnit 测试中失败

发布于 2024-11-26 11:28:42 字数 2305 浏览 1 评论 0原文

我有一个用 C# 4 编写的自动化插件(实现 Extensibility.IDTExtensibility2),我试图在其中加载一些(二进制)序列化数据。它在单元测试中完美运行,但在 Excel 中运行时失败:

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 

在 BinaryFormatter 上,我设置 AssemblyFormat(用于序列化和反序列化)如下:

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

我认为会忽略按照 此处

然后认为这可能是由于Excel的“受信任位置”概念,所以我添加了项目的目录并检查了所有子目录,但错误仍然存​​在。

我徒劳地尝试添加 System.Runtime.Serialization.OptionalFieldAttribute 属性,但没有帮助。

单元测试可以加载自己生成的序列化文件或在Excel中执行的相同代码,但Excel无论是否进行了实际序列化都无法加载序列化数据。

事实上,Excel 无法反序列化它自己序列化的内容,这暗示这是一个错误,因为它显然可以访问所使用的程序集。

所以问题是为什么 Excel 的反序列化与我的单元测试不同? (或者也许更重要的是;我怎样才能在 Excel 中进行反序列化?)

谢谢。

I have an automation addin (implementing Extensibility.IDTExtensibility2), written in C# 4, in which I'm trying to load some (binary) serialized data. It works perfectly in unit tests, but fails when running from within Excel:

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 

On the BinaryFormatter I'm setting the AssemblyFormat (for both serialization and deserialization) as follows:

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

Which I thought would ignore the version as per here.

Then thought it may be due to Excel's notion of "Trusted Location" so I added the project's directory and checked all subdirectories, but the error remained.

In vain I tried adding the System.Runtime.Serialization.OptionalFieldAttribute attribute, but it didn't help.

The unit tests can load the serialized files generated by itself or the same code executed in Excel, but Excel cannot load the serialized data regardless of whether it did the actual serialization.

The fact that Excel cannot deserialize what it has serialized itself hints that this is a redherring as it would obviously have access to the assembly used.

So the question is why does Excel deserialize differently compared to my unit test? (Or perhaps more importantly; How can I get deserialization working in Excel?)

thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

镜花水月 2024-12-03 11:28:42

我追逐红鲱鱼,但没有得到任何结果,假设这是一个版本控制问题(我查看了垫片和各种)。

事实上,这是由于奇怪的程序集绑定造成的;尽管自动化插件的 DLL 可以看到并加载它所依赖的 DLL 中的类,但当它可以反序列化时,它无法找到必要的程序集 - 即使反序列化调用来自属于它声称无法找到的 DLL 的类寻找!

解决方案当然是一个 hack,但它让我克服了这个问题,所以万一其他人像我一样陷入困境,这里是 hack(s):

在类 X 中,这取决于类 Y 和 Z,每个都来自各自的项目 DLL(名为ProjectForX、ProjectForY 和 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;
        }
    }

基本上它只是挂钩解析事件并根据请求的类型名称提供正确的程序集 - 这不是特别安全(我在程序集描述符中使用逗号来尝试最大程度地减少任何冲突) 。

几乎可以肯定有一个明确的方法可以做到这一点,而问题可能只是由于配置不佳以及我对 MS 产品(尤其是 C#)的有限经验造成的。

如果我只是构建了一个整体 DLL,这就不会成为问题 =)

I chased the red herring(s) and got no where with the assumption that it was a versioning issue (I'd looked at shims and all sorts).

It was in fact due to weird assembly binding; although the automation addin's DLL could see and load classes from the DLLs it depended on, when it can to deserialization it failed to find the necessary assemblies - even though the deserialization call was from a class belonging to a DLL that it claimed it couldn't find!

Solution is certainly a hack but it's got me past this, so in case anyone else is stuck as i was, here's the hack(s):

Inside a class X, that depends on classes Y and Z each from their respective project DLLs (named ProjectForX, ProjectForY and 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;
        }
    }

Basically it just hooks into the resolve events and provides the correct assembly based on the requested type name - this isn't particularly safe (I'm using the comma in the assembly descriptor to try to minimize any clashes).

There's almost certainly a clear way to do this, and the issue is probably just a result of poor configuration and my limited experience with MS products and C# in particular.

This wouldn't have been an issue if I'd just built a monolithic DLL =)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文