调用时,MSXMLl 6.0 XmlDOMNodeList失败GetEnumerator

时间:2017-03-06 13:08:49

标签: c# .net com marshalling msxml

我们的项目使用MSXML 6.0 com对象来处理XMLComImport属性。 com 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
  4. 此时我发现了第2点的三个解决方法(在Windows上使用带有WinRT的msxml)但不是我们的解决方案(对于具有相同问题的开发人员可能会有所帮助):

    1. 不要使用foreach和GetEnumerator。很清楚:)
    2. 使用ICustomMarshalerSelectNodes创建自定义编组,这将使用WinRT类型创建包装并通过for提供自定义GetEnumerator。
    3. 将msxml的版本从6更改为3.
    4. 我需要帮助才能在第2点找到问题的根源,并了解如何解决问题。 感谢

1 个答案:

答案 0 :(得分:1)

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

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