Использование, NET Generic List ToArray в COM Called Wrapper вызывает нарушение доступа, я что-то упустил? - PullRequest
3 голосов
/ 02 мая 2009

У меня проблема при попытке передать массив обратно в вызывающую COM.

По сути, у меня есть общий список классов, который я хочу вернуть вызывающему COM, вы не можете использовать обобщенные значения в COM, поэтому он должен быть строго типизирован. Я полагал, что просто возврат .ToArray () в Список даст мне то, что мне нужно.

Когда я использую его в VB6, он отлично работает. В отладчике. Когда я компилирую приложение и запускаю его как простой исполняемый файл, он падает с нарушением доступа к памяти. Я довольно озадачен. Я не могу перехватить какие-либо ошибки в VB или в компоненте .NET, поэтому я предполагаю, что что-то происходит в трансляции COM.

Сбой при "return a" в getList ()

Журнал событий приложений содержит эту ошибку:

Сбой приложения project1.exe, версия 1.0.0.0, штамп 49fb60d8, сбой модуля msvbvm60.dll, версия 6.0.98.2, штамп 4802a186, отладка? 0, адрес ошибки 0x00106154.

Вот код VB6 для справки, это глупо просто.

Private Sub Form_Load()
  Dim c1 As IClass1
  Dim c2

  Set c1 = New Class1

  For Each c2 In c1.getList
    Debug.Print c2.Value
  Next

End Sub

И код .NET:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace TestCOM
{

    public interface IClass1
    {
        List<IClass2> MyList { get; }

        IClass2[] getList();
    }

    public class Class1 : IClass1
    {
        public List<IClass2> MyList { get; private set; }

        public Class1()
        {
            this.MyList = new List<IClass2>();
            this.MyList.Add(new Class2());
            this.MyList.Add(new Class2());
        }

        public IClass2[] getList()
        {
            try
            {
                System.IO.File.WriteAllText(@"C:\COMLog1.txt", "Hi.");
                IClass2[] a = new IClass2[this.MyList.Count];
                System.IO.File.WriteAllText(@"C:\COMLog1.txt", "Bye.");
                for (int i = 0; i < this.MyList.Count; i++)
                {
                    a[i] = this.MyList[i];
                }
                System.IO.File.WriteAllText(@"C:\COMLog1.txt", "Sup.");

//The logging appears on disk all the way to here, so the failure appears to be in the interop layer.

                return a;
            }
            catch (Exception e)
            {
                System.IO.File.WriteAllText(@"C:\COMLog.txt", string.Format("Error:\n{0}", e.Message));
            }
            return null;
        }
    }

    public interface IClass2
    {
        int Value { get; }
    }
    public class Class2: IClass2
    {
        public int Value { get; private set; }

        public Class2()
        {
            Random r = new Random();
            this.Value = r.Next();
        }
    }
}

Проблема связана с поздним связыванием со стороны VB. Использование раннего связанного массива решает проблему, вот окончательный код VB для тестового приложения:

Private Sub Form_Load()
  Dim c1 As IClass1
  Dim c2() As IClass2
  Dim x

  Set c1 = New Class1

  c2 = c1.getList

  For Each x In c2
    Debug.Print x.Value
  Next

End Sub

1 Ответ

4 голосов
/ 02 мая 2009

Похоже, что проблема может быть в позднем и раннем связывании. Если вы объявляете c2 как тип, это должно устранить эту ошибку. Вот еще один сайт, посвященный аналогичной проблеме.

http://weblogs.asp.net/psteele/archive/2007/02/06/com-interop-does-not-like-uninitialized-arrays.aspx

Что касается невозможности зафиксировать ошибку, то похоже, что функция .ToArray для общих списков способна повредить данные. Вот код из отражателя:

//You can see that the function calls Array.Copy
Public Function ToArray() As T()
    Dim destinationArray As T() = New T(Me._size  - 1) {}
    Array.Copy(Me._items, 0, destinationArray, 0, Me._size)
    Return destinationArray
End Function

//You can see from the ReliabilityContract that this method is set to
//Consistency.MayCorruptInstance and Cer.MayFail
<ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)> _
Public Shared Sub Copy(ByVal sourceArray As Array, _
    ByVal sourceIndex As Integer, ByVal destinationArray As Array, _
    ByVal destinationIndex As Integer, ByVal length As Integer)
        Array.Copy(sourceArray, sourceIndex, destinationArray, _
        destinationIndex, length, False)
End Sub

Кроме того, вот некоторая документация, которая может помочь объяснить, почему вы не можете правильно перехватить ошибку. По сути, весь AppDomain может быть поврежден или может быть ThreadAbort или StackOverflow или какая-то другая критическая ошибка, которая приводит к прерыванию всей обработки. Вторая ссылка говорит о том, как решить эти типы проблем.

...