Массив VBA определяемых пользователем объектов из C# DLL

Справочная информация:
Я создаю SCADA-систему, работающую на VBA, и ищу возможности C#. Я создаю библиотеку DLL на C# и получаю основные данные для передачи между DLL и VBA.

[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)]
public class BE_Log
{
    public string DateTime
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }
    public string User
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }
    public string SCADA
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }
    public string Tag
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }
    public string Area1
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }
    public string Area2
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }
    public string Description
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }
    public string ValueOld
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }
    public string ValueNew
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }

    public BE_Log(string DataTime, string User, string SCADA, string Tag, string Area1, string Area2,string Description)
    {
        this.DateTime = DateTime;
        this.User = User;
        this.SCADA = SCADA;
        this.Tag = Tag;
        this.Area1 = Area1;
        this.Area2 = Area2;
        this.Description = Description;
    }

    public BE_Log(string DataTime, string User, string SCADA, string Tag, string Area1, string Area2, string Description, string ValueOld, string ValueNew)
    {
        this.DateTime = DateTime;
        this.User = User;
        this.SCADA = SCADA;
        this.Tag = Tag;
        this.Area1 = Area1;
        this.Area2 = Area2;
        this.Description = Description;
        this.ValueOld = ValueOld;
        this.ValueNew = ValueNew;
    }

}

И я вернул класс следующим образом:

[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)]
public class TI
{
    private BLL_LogBook bll;

    public TI()
    {
        bll = new BLL_LogBook();
    }

    [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_USERDEFINED)]  //  SafeArrayUserDefinedSubType = typeof(BE_Log)
    public BE_Log[] CreateLogBook()
    {
        List<BE_Log> logs = bll.GetLogEntry();
        return logs.ToArray();
    }
}

Мой слой данных:

public class BLL_LogBook
{
    public List<BE_Log> GetLogEntry()
    {
        List<BE_Log> logs = new List<BE_Log>();
        logs.Add(new BE_Log("05-05-2015", "some user", "scada01", "LA010NDA10CU12XQ12", "Ribe", "Esbjerg", "Some short description"));
        logs.Add(new BE_Log("06-05-2015", "test user", "scada01", "LA010NDA10CU12XB05", "Herning", "KBH", "Some long description"));
        logs.Add(new BE_Log("07-05-2015", "normal user", "scada02", "LA010NDA10CU12YQ01", "Åhus", "Tønder", "Some test description"));

        return logs;
    }
}

Статический метод VBA вызывает:

static class UnmanagedExports
{
    [DllExport]
    [return: MarshalAs(UnmanagedType.IDispatch)]
    static Object TI_Object()
    {
        return new TI();
    }
}

В VBA я получил данные таким образом:

Declare Function TI_Object Lib "<path>\\TJI.dll" () As Object

Sub TestTheTestClass()
    Dim TJI As Object
    Set TJI = TI_Object()

    Dim test As Variant
    test = TJI.CreateLogBook()

    Dim log As Variant
    Set log = test(0)

    Debug.Print log.User
End Sub

Теперь к моему вопросу:
Как вернуть массив или список класса 'BE_Log'
EDIT: Здесь я застрял: http://puu.sh/hnPGe/472ff863d0.png

Я безуспешно пытался разработать некоторые документы Microsoft.

Первоначальное руководство, которому я следовал, было следующим:
http://www.analystcave.com/excel-use-c-sharp-in-excel-vba/

Он утверждает следующее, однако я не совсем понимаю его.

Если вы используете массив в качестве аргумента, обязательно используйте параметр С# «ref» для получения по ссылке, например. ссылка int[] ар

Я думаю, это как-то связано с «MarshalAs» или с тем, как я читаю данные в VBA.


person Rasmus Plats    schedule 17.04.2015    source источник


Ответы (1)


Если вы используете массив в качестве аргумента, обязательно используйте параметр С# «ref» для получения по ссылке, например. ссылка int[] ар

Это верно, но у вас нет методов, которые принимают аргумент массива, поэтому это не применимо к вашей ситуации.

Вы пытались изменить подпись метода:

public Log CreateLogBook()

к подписи, которая возвращает массив:

public Log[] CreateLogBook()
person Joe    schedule 17.04.2015
comment
Я пробовал и с массивом, и со списком public Log[] CreateLogBook() public List<BE_Log> CreateLogBook() - person Rasmus Plats; 17.04.2015
comment
Я пробовал... - и что в результате? - person Joe; 17.04.2015
comment
Я не могу заставить VBA прочитать данные, или, может быть, я неправильно сделал маршал? Можете ли вы привести пример того, как читать массив в VBA из DLL? - person Rasmus Plats; 18.04.2015
comment
@RasmusPlats - что происходит, когда вы пытаетесь вызвать метод? Получаете ли вы исключение (ошибка выполнения VBA), и если да, то какое сообщение об ошибке? Вы получаете что-то неожиданное, и в этом случае проверьте его с помощью отладчика VBA. - person Joe; 19.04.2015
comment
Я получаю ошибку времени выполнения «424» (требуется объект) Dim test As Object Set test = DLL.CreateLogBook() - person Rasmus Plats; 20.04.2015
comment
Однако, если Dim тестирует как Variant, я могу пойти дальше. Но затем я получаю ошибку несоответствия типа: Dim log As Object Set log = test(0) - person Rasmus Plats; 20.04.2015
comment
Теперь вы делаете успехи. Dim test как Variant, вызовите метод CreateLogBook и проверьте возвращаемое значение test в контрольном окне VBA. - person Joe; 20.04.2015
comment
Отлично. Теперь он говорит: «Нет переменных» - person Rasmus Plats; 20.04.2015
comment
В порядке. Теперь я очень близко. Сделал еще несколько тестов и повозился. Теперь я вижу массив в VBA, однако я должен объявить его как вариант, а не объект. Поэтому я не могу получить свои данные. Dim log As Variant Set log = test(0) Debug.Print log.DateTime - person Rasmus Plats; 20.04.2015
comment
Похоже, ваш код С# возвращает массив BE_Log. В опубликованном вами коде не отображается определение BE_Log. - person Joe; 20.04.2015
comment
Извини. Я переименовал определение. Это тот же класс, что и Log. Но как мне получить элемент данных из этого массива? Когда я вернул только один журнал, я установил его как объект и мог получить доступ к данным. Это невозможно с Variant? - person Rasmus Plats; 20.04.2015
comment
Давайте продолжим обсуждение в чате. - person Rasmus Plats; 20.04.2015
comment
На снимке экрана показано «Нет переменных» для test(0) и test(1), которые имеют тип BE_Log. Я ожидал бы увидеть это, если бы BE_Log был типом коллекции, а коллекции были пустыми. Также вы получаете доступ к log.DateTime, который не является допустимым свойством опубликованного вами класса журнала. Было бы полезно, если бы вы опубликовали полный, непротиворечивый образец - см. stackoverflow.com/help/mcve. - person Joe; 20.04.2015
comment
Я вижу, что я сделал не так. Я уберу весь код до минимума и отредактирую свой вопрос с полным кодом. - person Rasmus Plats; 23.04.2015