我在ASP.net,c#中开发了一个Web服务,并在IIS上托管,这将由vba客户端使用。下载了Office 2003 Web Services 2.01工具包后,我遇到了成功创建所需代理类的问题(许多用户在线记录),并决定创建一个.net DLL库。我创建了一个库,它引用了Web服务并将其中一个方法暴露给c#中的公共函数。
我现在有三个问题:
如何在VBA中引用dll类?我试图转到Tools-> References并浏览到dll位置,但是我收到错误“无法添加对指定文件的引用”。磁盘上是否有特定位置我必须复制.dll?
我是否还可以复制dll文件旁边的dll.config文件,以便在那里设置端点网址?
由于要调用的方法是接受一个对象(由各种成员和几个List<>成员组成,这些如何在VBA代码中实现?
答案 0 :(得分:27)
您需要为程序集(DLL)创建一个COM可调用包装器(CCW)。 .NET互操作性是一个相当深入的主题,但实现起来相对容易。
首先,您需要确保整个程序集已注册COM interop。您可以通过选中“注册COM Interop”在Visual Studio的“Build”选项卡上执行此操作。其次,您应该在所有类中包含System.Runtime.InteropServices:
using System.Runtime.InteropServices;
接下来,您应该使用[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");
}
}
}
编译程序集后,您应该可以通过转到“工具>引用”在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。