從 vba 呼叫 .net 庫方法
我在 ASP.net、c# 中開發了一個 Web 服務,並託管在 IIS 上,供 vba 客戶端使用。下載了 Office 2003 Web Services 2.01 工具包後,我在成功創建所需的代理類時遇到了問題(許多使用者線上記錄),並決定改為創建一個 .net dll 庫。我已經創建了庫,它引用了 Web 服務並將其方法之一公開給 c# 中的公共函式。
我現在有三個問題:
- 如何在 VBA 中引用 dll 類?我試圖轉到工具-> 參考並瀏覽到 dll 位置,但我收到錯誤“無法添加對指定文件的引用”。磁碟上是否有我必須複製 .dll 的特定位置?
- 我還可以將 dll.config 文件複製到 dll 文件旁邊,以便在那裡有端點 url 嗎?
- 由於呼叫的方法是接受一個對象(由各種成員和幾個 List<> 成員組成,這些如何在 VBA 程式碼中實現?
您需要為您的程序集 (DLL) 創建一個可呼叫 COM 的包裝器 (CCW)。.NET 互操作性是一個相當深入的話題,但它相對容易實現。
首先,您需要確保您的整個程序集都註冊了 COM 互操作。您可以通過選中“註冊 COM 互操作”在 Visual Studio 的“建構”選項卡上執行此操作。其次,您應該在所有類中包含 System.Runtime.InteropServices:
using System.Runtime.InteropServices;
[Serializable(), ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)]接下來,您應該使用屬性裝飾所有要公開的類。這將使您可以正確訪問類成員並在 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。
然後你應該能夠訪問你的主類和任何其他類,如下所示: