Dot-Net

從 vba 呼叫 .net 庫方法

  • November 13, 2013

我在 ASP.net、c# 中開發了一個 Web 服務,並託管在 IIS 上,供 vba 客戶端使用。下載了 Office 2003 Web Services 2.01 工具包後,我在成功創建所需的代理類時遇到了問題(許多使用者線上記錄),並決定改為創建一個 .net dll 庫。我已經創建了庫,它引用了 Web 服務並將其方法之一公開給 c# 中的公共函式。

我現在有三個問題:

  1. 如何在 VBA 中引用 dll 類?我試圖轉到工具-> 參考並瀏覽到 dll 位置,但我收到錯誤“無法添加對指定文件的引用”。磁碟上是否有我必須複製 .dll 的特定位置?
  2. 我還可以將 dll.config 文件複製到 dll 文件旁邊,以便在那裡有端點 url 嗎?
  3. 由於呼叫的方法是接受一個對象(由各種成員和幾個 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&lt;Widget&gt; widgets = new List&lt;Widget&gt;();

       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。

引用自:https://stackoverflow.com/questions/19954001