从vba调用.net库方法

use*_*598 14 .net com vba com-callable-wrapper

我在ASP.net,c#中开发了一个Web服务,并在IIS上托管,由vba客户端使用.下载了Office 2003 Web Services 2.01工具包后,我遇到了成功创建所需代理类的问题(许多用户在线记录),并决定创建一个.net DLL库.我创建了一个库,它引用了Web服务并将其中一个方法暴露给c#中的公共函数.

我现在有三个问题:

  1. 如何在VBA中引用dll类?我试图去工具 - >引用并浏览到dll位置,但是我收到错误"无法添加对指定文件的引用".磁盘上是否有特定位置我必须复制.dll?

  2. 我还可以复制dll文件旁边的dll.config文件,以便在那里使用端点URL吗?

  3. 由于要调用的方法是接受一个对象(由各种成员和几个List <>成员组成,这些如何在VBA代码中实现?

ror*_*.ap 27

您需要为程序集(DLL)创建一个COM可调用包装器(CCW)..NET互操作性是一个相当深入的主题,但实现某些功能相对容易.

首先,您需要确保整个程序集已注册COM interop.您可以通过选中"注册COM Interop"在Visual Studio的"Build"选项卡上执行此操作.其次,您应该在所有类中包含System.Runtime.InteropServices:

using System.Runtime.InteropServices;
Run Code Online (Sandbox Code Playgroud)

接下来,您应该使用[Serializable(), ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)]属性修饰要公开的所有类.这将使您可以正确访问类成员并在VBA编辑器中使用intellisense.

你需要有一个入口点 - 即一个主类,并且该类应该有一个没有参数的公共构造函数.从该类中,您可以调用返回其他类的实例的方法.这是一个简单的例子:

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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编译程序集后,您应该可以通过转到"工具>引用"在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
Run Code Online (Sandbox Code Playgroud)

要回答关于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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以在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
Run Code Online (Sandbox Code Playgroud)

注意:我已经包含System.Collections;在新项目中,因此我的WidgetCollection类可以实现IEnumerable.

  • 对于读取上述注释的任何人:当您调用返回值(即函数)的方法或使用`Call`关键字时,VBA中只需要括号括号.如果在调用*not*返回值的方法时使用括号(并且您没有使用`Call`),VBA将评估括号中包含的任何内容作为表达式,并将该求值的结果传递给方法.通常这会导致"类型不匹配"错误. (5认同)
  • @jimtollan——谢谢。很高兴听到这个。我确实花了很长时间才弄清楚这一切并让它发挥作用,所以我真的很高兴我帮助了这么多面临同样问题的人。这就是 SO 的精髓所在。 (2认同)