Llamar a un método de biblioteca .net desde vba

Desarrollé un servicio web en ASP.net, c # y alojado en IIS, que debe ser consumido por un cliente vba. Al haber descargado Office 2003 Web Services 2.01 Toolkit, encontré un problema al crear con éxito las clases de proxy requeridas (según lo documentado por muchos usuarios en línea), y decidí crear una biblioteca .NET dll en su lugar. Creé la biblioteca, que hace referencia al servicio web y expone uno de sus métodos a una función pública en c #.

Ahora tengo tres preguntas:

  1. ¿Cómo hago referencia a la clase dll en VBA? Traté de ir a Herramientas-> Referencias y navegué a la ubicación de dll, pero aparece el error “No puedo agregar referencia al archivo especificado”. ¿Hay una ubicación específica en el disco que tengo que copiar el .dll?

  2. ¿Puedo también copiar el archivo dll.config junto al archivo dll, para tener allí la URL del punto final?

  3. Dado que el método para llamar es aceptar un objeto (que consta de varios miembros y un par de miembros List , ¿cómo se implementarán en el código VBA?

Tendrá que crear un contenedor COM-callable (CCW) para su ensamblado (DLL). La interoperabilidad .NET es un tema bastante profundo, pero es relativamente fácil lograr que algo despegue.

En primer lugar, debe asegurarse de que todo su ensamblaje esté registrado para interoperabilidad COM. Puede hacer esto en la pestaña “Crear” en Visual Studio marcando “Registrarse para Interoperabilidad COM”. En segundo lugar, debe incluir System.Runtime.InteropServices en todas sus clases:

using System.Runtime.InteropServices; 

A continuación, debe decorar todas las clases que desea que se expongan con los [Serializable(), ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)] . Esto lo hará para que pueda acceder a los miembros de la clase correctamente y usar intellisense desde el editor de VBA.

Necesita tener un punto de entrada, es decir, una clase principal, y esa clase debe tener un constructor público sin argumentos. Desde esa clase, puede llamar a métodos que devuelven instancias de sus otras clases. Aquí hay un ejemplo simple:

 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"); } } } 

Una vez que compile su ensamblaje, debería poder incluir una referencia en VBA yendo a “Herramientas> Referencias”:

enter image description here Entonces deberías poder acceder a tu clase principal y a cualquier otra clase como esta:

 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 

Para responder a su pregunta sobre List <>: COM no sabe nada sobre genéricos, por lo que no son compatibles. De hecho, el uso de matrices en CCW es incluso un tema complicado. En mi experiencia, he encontrado que lo más fácil es crear mis propias clases de colección. Utilizando el ejemplo anterior, podría crear una clase WidgetCollection. Aquí hay un proyecto ligeramente modificado con la clase WidgetCollection incluida:

 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 widgets = new List(); 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); } } } 

Y puedes usarlo así en 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 

NOTA: He incluido System.Collections; en el nuevo proyecto, entonces mi clase WidgetCollection puede implementar IEnumerable.