【问题标题】:MSXMLl 6.0 XmlDOMNodeList fails when is invoked GetEnumeratorMSXMLl 6.0 XmlDOMNodeList 在调用 GetEnumerator 时失败
【发布时间】:2017-07-26 08:57:56
【问题描述】:

我们的项目使用MSXML 6.0 com 对象来处理XMLComImport 属性。下面的 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()

由于执行SelectNodes 获得的对象resultQuery 存在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 点的三个解决方法(在带有 WinRT 的 Windows 上使用 msxml),但不是我们的解决方案(这对有相同问题的开发人员可能会有所帮助):

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

我需要帮助来找到第 2 点中的问题根源并了解如何解决它。 谢谢

【问题讨论】:

    标签: c# .net com marshalling msxml


    【解决方案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 的所有问题。

    【讨论】:

    • Artsiom 感谢您提供此信息。我正在尝试使用 MSXML 6 做完全相同的事情(退回到 MSXML 3 对我来说是不可行的)。我也试图遍历一组检索到的节点。你的答案不是很清楚,我希望我不必去解构 DLL,添加到 IXMLDOMNodeList。这似乎比预期的要深得多,以避免当前 MSXML 版本中的问题。你有没有发现任何其他的编码方法?我在 .NET 框架中使用 VBScript。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 1970-01-01
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 2020-06-22
    • 2016-02-20
    相关资源
    最近更新 更多