MSXMLl 6.0 XmlDOMNodeList 在调用 GetEnumerator 时失败
MSXMLl 6.0 XmlDOMNodeList fails when is invoked GetEnumerator
我们的项目使用 MSXML 6.0
com 对象通过 ComImport
属性处理 XML
。下面的 com Class 提供对现有 MSXML COM
的访问(我只留下 SelectNodes 来澄清问题)。
[ComImport]
[ComSourceInterfaces("MSXML2.XMLDOMDocumentEvents")]
[TypeLibType(TypeLibTypeFlags.FCanCreate)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("88d96a06-f192-11d4-a65f-0040963251e5")]
public class FreeThreadedDOMDocumentClass : IXMLDOMDocument2
{
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x1d)]
public extern object SelectNodes([In, MarshalAs(UnmanagedType.BStr)] string queryString);
}
[ComImport, Guid("2933BF95-7B36-11D2-B20E-00C04F983E60"), TypeLibType((short)0x10c0)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IXMLDOMDocument2
{
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x1d)]
object SelectNodes([In, MarshalAs(UnmanagedType.BStr)] string queryString);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix"), ComImport]
[Guid("2933BF82-7B36-11D2-B20E-00C04F983E60")]
[TypeLibType(TypeLibTypeFlags.FDispatchable)]//(short) 0x10c0
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IXMLDOMNodeList: IEnumerable
{
[DispId(0)]
IXMLDOMNode this[int index]
{
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0)]
get;
}
[DispId(0x4a)]
int Count
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0x4a)]
get;
}
}
而且我们的部分代码有问题。示例:
string xmlText = "<Item> <Element></Element></Item>";
IXMLDOMDocument2 domDocument = new FreeThreadedDOMDocumentClass();
domDocument.LoadXml(xmlText);
string xPath = "//Item";
IXMLDOMNodeList resultQuery = domDocument.SelectNodes(xPath) as IXMLDOMNodeList;
resultQuery.GetEnumerator()
由于执行 SelectNodes
而获得的对象 resultQuery
根据某些外部因素,GetEnumerator 有问题:
在windows7及以上早期系统(不支持WinRT技术的系统)
- 获得的对象
resultQuery
具有类型System.__ComObject
- resultQuery.GetEnumerator() 执行没有任何问题并提供有效的 Enumerator
在windows8以上的系统中(支持WinRT技术的系统)
- 获得的对象
resultQuery
的类型为 Windows.Data.Xml.Dom.XmlNodeList
。这是 WinRt 类型。
- resultQuery.GetEnumerator() 抛出异常:
System.ArgumentException: The object's type must not be a Windows Runtime type
。我发现异常来源是 Marshal.GetComObjectData
。这意味着我们的编组过程失败
在windows8以上的系统中导入com版本MSXML 3.0
:
- 获得的对象
resultQuery
具有类型System.__ComObject
- resultQuery.GetEnumerator() 执行没有任何问题并提供有效的 Enumerator
此时我找到了第 2 点的三个解决方法(在 windows 上使用 msxml 和 WinRT)但不是我们的解决方案(它可以帮助开发人员遇到同样的问题):
- 不要使用 foreach 和 GetEnumerator。很清楚:)
- 使用 ICustomMarshaler 为
SelectNodes
创建自定义编组,这将在 WinRT 类型上创建包装器并通过 for
. 提供自定义 GetEnumerator
- 将 msxml 的版本从 6 更改为 3。
我需要帮助来找到第 2 点中的问题根源并了解如何修复它。
谢谢
经过深入调查,我找到了解决方案:
1) 我使用 TLBimp.exe
为 msxml6 com 生成了互操作 C# dll
2) 使用反编译器打开生成的 dll
3) 找到生成的接口 IXMLDOMNodeList
。
它有下一个实现:
[Guid("2933BF82-7B36-11D2-B20E-00C04F983E60"), TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FNonExtensible | TypeLibTypeFlags.FDispatchable)]
[ComImport]
public interface IXMLDOMNodeList : IEnumerable
{
// Token: 0x1700012B RID: 299
[DispId(0)]
IXMLDOMNode this[[In] int index]
{
[DispId(0)]
[MethodImpl(MethodImplOptions.InternalCall)]
[return: MarshalAs(UnmanagedType.Interface)]
get;
}
// Token: 0x1700012C RID: 300
// (get) Token: 0x060003D5 RID: 981
[DispId(74)]
int length
{
[DispId(74)]
[MethodImpl(MethodImplOptions.InternalCall)]
get;
}
// Token: 0x060003D6 RID: 982
[DispId(76)]
[MethodImpl(MethodImplOptions.InternalCall)]
[return: MarshalAs(UnmanagedType.Interface)]
IXMLDOMNode nextNode();
// Token: 0x060003D7 RID: 983
[DispId(77)]
[MethodImpl(MethodImplOptions.InternalCall)]
void reset();
// Token: 0x060003D8 RID: 984
[TypeLibFunc(TypeLibFuncFlags.FRestricted | TypeLibFuncFlags.FHidden), DispId(-4)]
[MethodImpl(MethodImplOptions.InternalCall)]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(EnumeratorToEnumVariantMarshaler))]
IEnumerator GetEnumerator();
}
我们如何才能看到生成的接口有一些额外的方法:nextNode
reset
和覆盖 'GetEnumerator'
我在 IXMLDOMNodeList
的实现中添加了遗漏的方法,这解决了 WinRT 系统中 GetEnumerator
的所有问题。
我们的项目使用 MSXML 6.0
com 对象通过 ComImport
属性处理 XML
。下面的 com Class 提供对现有 MSXML COM
的访问(我只留下 SelectNodes 来澄清问题)。
[ComImport]
[ComSourceInterfaces("MSXML2.XMLDOMDocumentEvents")]
[TypeLibType(TypeLibTypeFlags.FCanCreate)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("88d96a06-f192-11d4-a65f-0040963251e5")]
public class FreeThreadedDOMDocumentClass : IXMLDOMDocument2
{
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x1d)]
public extern object SelectNodes([In, MarshalAs(UnmanagedType.BStr)] string queryString);
}
[ComImport, Guid("2933BF95-7B36-11D2-B20E-00C04F983E60"), TypeLibType((short)0x10c0)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IXMLDOMDocument2
{
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x1d)]
object SelectNodes([In, MarshalAs(UnmanagedType.BStr)] string queryString);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix"), ComImport]
[Guid("2933BF82-7B36-11D2-B20E-00C04F983E60")]
[TypeLibType(TypeLibTypeFlags.FDispatchable)]//(short) 0x10c0
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IXMLDOMNodeList: IEnumerable
{
[DispId(0)]
IXMLDOMNode this[int index]
{
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0)]
get;
}
[DispId(0x4a)]
int Count
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0x4a)]
get;
}
}
而且我们的部分代码有问题。示例:
string xmlText = "<Item> <Element></Element></Item>";
IXMLDOMDocument2 domDocument = new FreeThreadedDOMDocumentClass();
domDocument.LoadXml(xmlText);
string xPath = "//Item";
IXMLDOMNodeList resultQuery = domDocument.SelectNodes(xPath) as IXMLDOMNodeList;
resultQuery.GetEnumerator()
由于执行 SelectNodes
而获得的对象 resultQuery
根据某些外部因素,GetEnumerator 有问题:
在windows7及以上早期系统(不支持WinRT技术的系统)
- 获得的对象
resultQuery
具有类型System.__ComObject
- resultQuery.GetEnumerator() 执行没有任何问题并提供有效的 Enumerator
- 获得的对象
在windows8以上的系统中(支持WinRT技术的系统)
- 获得的对象
resultQuery
的类型为Windows.Data.Xml.Dom.XmlNodeList
。这是 WinRt 类型。 - resultQuery.GetEnumerator() 抛出异常:
System.ArgumentException: The object's type must not be a Windows Runtime type
。我发现异常来源是Marshal.GetComObjectData
。这意味着我们的编组过程失败
- 获得的对象
在windows8以上的系统中导入com版本
MSXML 3.0
:- 获得的对象
resultQuery
具有类型System.__ComObject
- resultQuery.GetEnumerator() 执行没有任何问题并提供有效的 Enumerator
- 获得的对象
此时我找到了第 2 点的三个解决方法(在 windows 上使用 msxml 和 WinRT)但不是我们的解决方案(它可以帮助开发人员遇到同样的问题):
- 不要使用 foreach 和 GetEnumerator。很清楚:)
- 使用 ICustomMarshaler 为
SelectNodes
创建自定义编组,这将在 WinRT 类型上创建包装器并通过for
. 提供自定义 GetEnumerator
- 将 msxml 的版本从 6 更改为 3。
我需要帮助来找到第 2 点中的问题根源并了解如何修复它。 谢谢
经过深入调查,我找到了解决方案:
1) 我使用 TLBimp.exe
为 msxml6 com 生成了互操作 C# dll2) 使用反编译器打开生成的 dll
3) 找到生成的接口 IXMLDOMNodeList
。
它有下一个实现:
[Guid("2933BF82-7B36-11D2-B20E-00C04F983E60"), TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FNonExtensible | TypeLibTypeFlags.FDispatchable)]
[ComImport]
public interface IXMLDOMNodeList : IEnumerable
{
// Token: 0x1700012B RID: 299
[DispId(0)]
IXMLDOMNode this[[In] int index]
{
[DispId(0)]
[MethodImpl(MethodImplOptions.InternalCall)]
[return: MarshalAs(UnmanagedType.Interface)]
get;
}
// Token: 0x1700012C RID: 300
// (get) Token: 0x060003D5 RID: 981
[DispId(74)]
int length
{
[DispId(74)]
[MethodImpl(MethodImplOptions.InternalCall)]
get;
}
// Token: 0x060003D6 RID: 982
[DispId(76)]
[MethodImpl(MethodImplOptions.InternalCall)]
[return: MarshalAs(UnmanagedType.Interface)]
IXMLDOMNode nextNode();
// Token: 0x060003D7 RID: 983
[DispId(77)]
[MethodImpl(MethodImplOptions.InternalCall)]
void reset();
// Token: 0x060003D8 RID: 984
[TypeLibFunc(TypeLibFuncFlags.FRestricted | TypeLibFuncFlags.FHidden), DispId(-4)]
[MethodImpl(MethodImplOptions.InternalCall)]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(EnumeratorToEnumVariantMarshaler))]
IEnumerator GetEnumerator();
}
我们如何才能看到生成的接口有一些额外的方法:nextNode
reset
和覆盖 'GetEnumerator'
我在 IXMLDOMNodeList
的实现中添加了遗漏的方法,这解决了 WinRT 系统中 GetEnumerator
的所有问题。