Вызов метода библиотеки.net из vba

Я разработал веб-сервис на ASP.net, C# и размещен на IIS, который должен использоваться клиентом vba. Загрузив инструментарий Office 2003 Web Services 2.01, я столкнулся с проблемой успешного создания необходимых классов прокси-серверов (что было задокументировано многими пользователями в Интернете) и решил вместо этого создать библиотеку.net dll. Я создал библиотеку, которая ссылается на веб-сервис и предоставляет один из его методов для публичной функции в C#.

Теперь у меня есть три вопроса:

  1. Как я могу ссылаться на класс DLL в VBA? Я попытался перейти к Tools->References и перешел к расположению DLL, но я получаю сообщение об ошибке "не могу добавить ссылку на указанный файл". Есть ли конкретное место на диске, я должен скопировать.dll?

  2. Могу ли я также скопировать файл dll.config рядом с файлом dll, чтобы URL-адрес конечной точки был там?

  3. Поскольку вызываемый метод принимает объект (состоящий из различных членов и нескольких членов List<>, как они должны быть реализованы в коде VBA?

1 ответ

Решение

Вам нужно будет создать COM-вызываемую оболочку (CCW) для вашей сборки (DLL). Взаимодействие.NET - это достаточно глубокая тема, но ее относительно легко получить с нуля.

Прежде всего, вам необходимо убедиться, что вся ваша сборка зарегистрирована для COM-взаимодействия. Вы можете сделать это на вкладке "Сборка" в Visual Studio, установив флажок "Зарегистрироваться для COM-взаимодействия". Во-вторых, вы должны включить System.Runtime.InteropServices во все ваши классы:

using System.Runtime.InteropServices;

Затем вы должны украсить все классы, которые вы хотите показать с помощью [Serializable(), ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)] атрибутов. Это позволит вам правильно обращаться к членам класса и использовать intellisense из редактора VBA.

У вас должна быть точка входа - то есть основной класс, и у этого класса должен быть открытый конструктор без аргументов. Из этого класса вы можете вызывать методы, которые возвращают экземпляры других ваших классов. Вот простой пример:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MyCCWTest
{
    [Serializable(),  ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)]
    public class Main
    {
        public Widget GetWidget()
        {
            return new Widget();
        }
    }

    [Serializable(), ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)]
    public class Widget
    {
        public void SayMyName()
        {
            MessageBox.Show("Widget 123");
        }
    }
}

Как только вы скомпилируете свою сборку, вы сможете включить ссылку на нее в VBA, перейдя в "Инструменты> Ссылки":

Тогда вы сможете получить доступ к своему основному классу и любым другим классам, подобным этому:

Sub Test()
    Dim main As MyCCWTest.main
    Set main = New MyCCWTest.main
    Dim myWidget As MyCCWTest.Widget
    Set myWidget = main.GetWidget
    myWidget.SayMyName
End Sub

Чтобы ответить на ваш вопрос о List<>: COM ничего не знает о дженериках, поэтому они не поддерживаются. На самом деле, использование массивов в CCW - даже сложная тема. По своему опыту я обнаружил, что самое простое, что нужно сделать, - это создать собственные классы коллекций. Используя приведенный выше пример, я мог бы создать класс WidgetCollection. Вот слегка измененный проект с включенным классом WidgetCollection:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MyCCWTest
{
    [Serializable(),  ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)]
    public class Main
    {
        private WidgetCollection myWidgets = new WidgetCollection();

        public Main()
        {
            myWidgets.Add(new Widget("Bob"));
            myWidgets.Add(new Widget("John"));
            myWidgets.Add(new Widget("Mary"));
        }

        public WidgetCollection MyWidgets
        {
            get
            {
                return myWidgets;
            }
        }
    }

    [Serializable(), ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)]
    public class Widget
    {
        private string myName;

        public Widget(string myName)
        {
            this.myName = myName;
        }

        public void SayMyName()
        {
            MessageBox.Show(myName);
        }
    }

    [Serializable(), ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)]
    public class WidgetCollection : IEnumerable
    {
        private List<Widget> widgets = new List<Widget>();

        public IEnumerator GetEnumerator()
        {
            return widgets.GetEnumerator();
        }

        public Widget this[int index]
        {
            get
            {
                return widgets[index];
            }
        }

        public int Count
        {
            get
            {
                return widgets.Count;
            }
        }

        public void Add(Widget item)
        {
            widgets.Add(item);
        }

        public void Remove(Widget item)
        {
            widgets.Remove(item);
        }
    }
}

И вы можете использовать это в VBA следующим образом:

Sub Test()
    Dim main As MyCCWTest.main
    Set main = New MyCCWTest.main
    Dim singleWidget As MyCCWTest.Widget

    For Each singleWidget In main.myWidgets
       singleWidget.SayMyName
    Next
End Sub

ПРИМЕЧАНИЕ: я включил System.Collections; в новом проекте, чтобы мой класс WidgetCollection мог реализовывать IEnumerable.

Другие вопросы по тегам