调用GetEnumerator时,MSXMLl 6.0 XmlDOMNodeList失败

问题描述 投票:1回答:1

我们的项目使用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有问题取决于一些外部因素:

  1. 在Windows 7或更早的系统中(不支持WinRT技术的系统) 获得的对象resultQuery有类型System.__ComObject resultQuery.GetEnumerator()执行没有任何问题,并提供有效的Enumerator
  2. 在Windows 8或更高版本的系统中(支持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。这意味着我们的编组过程失败了
  3. 在Windows 8或更高版本的系统中使用导入的com MSXML 3.0版本: 获得的对象resultQuery有类型System.__ComObject resultQuery.GetEnumerator()执行没有任何问题,并提供有效的Enumerator

此时我发现了第2点的三个解决方法(在Windows上使用带有WinRT的msxml)但不是我们的解决方案(对于具有相同问题的开发人员可能会有所帮助):

  1. 不要使用foreach和GetEnumerator。很明显 :)
  2. 使用SelectNodesICustomMarshaler创建自定义编组,它将创建WinRT类型的包装并通过for提供自定义GetEnumerator。
  3. 将msxml的版本从6更改为3。

我需要帮助才能在第2点找到问题的根源,并了解如何解决问题。谢谢

c# .net com marshalling msxml
1个回答
1
投票

深入调查后,我找到了解决方案:

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的所有问题。

© www.soinside.com 2019 - 2024. All rights reserved.