我们的项目使用MSXML 6.0
com对象通过XML
属性与ComImport
一起工作。下面的com类提供对现有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()
获取对象resultQuery
作为执行SelectNodes
的结果与GetEnumerator有问题取决于一些外部因素:
resultQuery
有类型System.__ComObject
resultQuery.GetEnumerator()执行没有任何问题,并提供有效的EnumeratorresultQuery
有类型Windows.Data.Xml.Dom.XmlNodeList
。这是WinRt类型。
resultQuery.GetEnumerator()抛出异常:System.ArgumentException: The object's type must not be a Windows Runtime type
。我发现异常来源是Marshal.GetComObjectData
。这意味着我们的编组过程失败了MSXML 3.0
版本:
获得的对象resultQuery
有类型System.__ComObject
resultQuery.GetEnumerator()执行没有任何问题,并提供有效的Enumerator此时我发现了第2点的三个解决方法(在Windows上使用带有WinRT的msxml)但不是我们的解决方案(对于具有相同问题的开发人员可能会有所帮助):
SelectNodes
为ICustomMarshaler创建自定义编组,它将创建WinRT类型的包装并通过for
提供自定义GetEnumerator。我需要帮助才能在第2点找到问题的根源,并了解如何解决问题。谢谢
深入调查后,我找到了解决方案:
1)我使用TLBimp.exe为msxml6 com生成了interop 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();
}
我们怎样才能看到生成的接口有一些额外的方法:qazxsw poi nextNode
和覆盖'GetEnumerator'
我为我的reset
实现提供了错过的方法,这解决了使用WinRT的系统中IXMLDOMNodeList
的所有问题。