0

我正在尝试将 xml 字符串反序列化为 DataTable。当我从 .NET(WinForms 项目)调用代码时,代码运行良好,但如果我从 Word VBA 调用代码,我会收到一个异常,提示“值不能为空”。

为了清楚起见,我的 Winforms 项目是一个管理模块,而 COM 可见库是我的客户端。Admin 模块没有对我的 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 输入中有一个自定义类型。

猜测开始:

  1. 这在我的 WinForms 项目中起作用的原因是 DLL 和类型对于 xml 序列化程序是“已知的”,但是当从 VBA 调用时,序列化程序无法找到/创建类型。注意:该类型已经在 WinForms 项目和 COM 库的代码中使用,因此对于任何一个项目来说都不是“未知”的。
  2. 不知何故,当从 VBA 调用时,序列化程序使用不同的编码,这与 xml 输入混淆。

我决定不包含 xml 输入,因为我已经确定它可以反序列化 - 而且我只将它作为 xml 转义字符串,因此很难“理解”。

我希望有人可以帮助我,因为我对此感到非常沮丧。如果您有任何问题或觉得需要更多信息,请随时提出。

4

1 回答 1

0

我从未找到发生这种情况的实际原因,但我找到了解决方案。这不是最漂亮的解决方案,但它确实有效,希望这可以为其他人节省大量时间。

我推测不知何故找不到类型,这几乎是真的。实际问题是找不到包含我的类型的程序集。在调用 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;
}

我仍然对解释感兴趣,所以如果有人有解释,我会全神贯注 :) 另外,解决方案不是很漂亮,所以如果有人有更好的建议,请分享。

于 2015-03-13T16:46:12.247 回答