我正在尝试将xml字符串反序列化为数据表。当我从.NET(WinForms项目)调用代码时,代码运行良好,但是如果我从Word VBA调用代码,则会收到异常,指出"值不能为空"。
需要明确的是,我的Winforms项目是一个管理模块,COM可见库是我的客户端。管理模块没有对我的 COM 库的引用。COM 库主要是我的其他类的包装器。
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Data.Common.ObjectStorage.ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)
at System.Data.XmlDataLoader.LoadColumn(DataColumn column, Object[] foundColumns)
at System.Data.XmlDataLoader.LoadTable(DataTable table, Boolean isNested)
at System.Data.XmlDataLoader.LoadData(XmlReader reader)
at System.Data.DataTable.ReadXml(XmlReader reader, Boolean denyResolving)
at System.Data.DataTable.ReadXml(TextReader reader)
以下代码是我用来反序列化表的代码。
var table = new DataTable();
using (var stringReader = new StringReader(tableXmlString))
table.ReadXml(stringReader);
我的第一个想法当然是 xml 是错误的,所以我尝试使用硬编码字符串,首先在我的 WinForms 项目中,以便我知道它有效,然后在我的 COM 可见库中。我通过编写一些代码来使用硬编码字符串进行反序列化,然后从 VBA 调用该方法,但它仍然失败。
经过大量测试,我得出的结论是,它一定与从 VBA 调用的代码有关,以证明该理论我创建了一个新的测试 WinForms 项目,添加了对我的 COM 库的引用,并以与 VBA 相同的方式运行代码。正如我所料,没有例外,反序列化进展顺利。
最后一条信息是我在 xml 输入中有一个自定义类型。
猜测开始了:
- 这在我的 WinForms 项目中工作的原因是 DLL并且类型对于 XML 序列化程序是"已知的",但是从 VBA 调用时不知何故,序列化程序无法找到/创建类型。注意:类型已经在 WinForms 项目和COM 库,因此它对任何一个项目都不是"未知的"。
- 不知何故,当从 VBA 调用时,序列化程序使用不同的与 xml 输入混淆的编码。
我决定不包括 xml 输入,因为我已经确定它可以反序列化 - 我也只将其作为 xml 转义字符串,使其难以"理解"。
我希望有人可以帮助我,因为我对此感到非常沮丧。如果您有任何问题或觉得需要更多信息,请随时询问。
我从未找到发生这种情况的实际原因,但我已经找到了解决方案。这不是最漂亮的解决方案,但它有效,希望这将为其他人节省大量时间。
我推测不知何故找不到类型,这几乎是真的。实际问题是找不到包含我的类型的程序集。在调用 DataTable.ReadXml 之前进行测试时,我可以看到程序集实际上已加载到 AppDomain 中,但是一旦进入 System.Data 库,如果我从 VBA 调用,情况就不再是这样了。
我怀疑DataTable中的代码实际上是在Word AppDomain的上下文中运行的,但我不确定。
所有这些的解决方案是通过挂接到 AppDomain.CurrentDomain.AssemblyResolve 并使用以下代码来手动解析程序集:
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
var myType = typeof(MyType);
var assembly = myType.Assembly;
//Get the assembly name with comma. I do not care about version in this instance, but it is generally a good idea to include.
//ex: MyTypeAssembly,
var subString = assembly.FullName.Substring(0, assembly.FullName.IndexOf(',') + 1);
if (args.Name.StartsWith(subString))
return myType.Assembly;
}
catch
{
return null;
}
return null;
}
我仍然对解释感兴趣,所以如果有人有一个,我都听:)此外,该解决方案不是很漂亮,所以如果有人有更好的建议,请分享它。