web-dev-qa-db-ja.com

Canonical:Excel VBAから.NETメソッドを呼び出す方法

VBAマクロから.NET 2コードを直接呼び出す方法を見つけました。

Dim clr As mscoree.CorRuntimeHost
Set clr = New mscoree.CorRuntimeHost
clr.Start
Dim domain As mscorlib.AppDomain
clr.GetDefaultDomain domain
Dim myInstanceOfDotNetClass As Object
Set myInstanceOfDotNetClass = domain.CreateInstanceFrom("SomeDotNetAssembly.dll", "Namespace.Typename").Unwrap
Call myInstanceOfDotNetClass.ExecuteSomeDotNetMethod

(このコードを機能させるには、Excelの[ツール]-> [参照設定]を使用して、mscoree.tlbおよびmscorlib.tlbへの参照をExcel VBAに追加する必要がありました)

ただし、これは.NET Frameworkバージョン3.5までの.NET CLR 2アセンブリでのみ機能します。

次に、.NET 4で動作するようにする必要があります。

.NET CLR4が別のバージョンに依存しないランタイムのインスタンスを作成する方法を導入し、C++で記述されたかなり簡単なコード例を見つけたことを理解しました: http://dev.widemeadows.de/ 2014/02/04/hosting-the-net-4-runtime-in-a-native-process /

しかし、私のExcel VBAスキルは、これらの数行のコードを有効なVBAマクロに変換するのに十分ではありません。誰でも私を助けてくれますか?

22
user1983691

デフォルトポリシーは、CLR 4がCLR 2からレガシーコードを実行できないようにします。

Set clr = New mscoree.CorRuntimeHost

レガシー実行を有効にするには、Excel.exe.configがあるフォルダーにファイルExcel.exeを作成できます。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0"/>
  </startup>
</configuration>

または、New mscoree.CorRuntimeHostの代わりにネイティブ関数CorBindToRuntimeExを呼び出すことができます。

Private Declare PtrSafe Function CorBindToRuntimeEx Lib "mscoree" ( _
    ByVal pwszVersion As LongPtr, _
    ByVal pwszBuildFlavor As LongPtr, _
    ByVal startupFlags As Long, _
    ByRef rclsid As Long, _
    ByRef riid As Long, _
    ByRef ppvObject As mscoree.CorRuntimeHost) As Long

Private Declare PtrSafe Function VariantCopy Lib "oleaut32" (dest, src) As Long


''
' Creates a .Net object with the CLR 4 without registration.  '
''
Function CreateInstance(Assembly As String, typeName As String) As Variant
  Const CLR$ = "v4.0.30319"

  Static domain As mscorlib.AppDomain
  If domain Is Nothing Then
    Dim Host As mscoree.CorRuntimeHost, hr&, T&(0 To 7)
    T(0) = &HCB2F6723: T(1) = &H11D2AB3A: T(2) = &HC000409C: T(3) = &H3E0AA34F
    T(4) = &HCB2F6722: T(5) = &H11D2AB3A: T(6) = &HC000409C: T(7) = &H3E0AA34F

    hr = CorBindToRuntimeEx(StrPtr(CLR), 0, 3, T(0), T(4), Host)
    If hr And -2 Then err.Raise hr

    Host.Start
    Host.GetDefaultDomain domain
  End If

  VariantCopy CreateInstance, domain.CreateInstanceFrom(Assembly, typeName).Unwrap
End Function
5
Florent B.

これは、Excel(またはVBA)から.Netを呼び出すための3つの主要な方法に関する 標準 の回答です。

3つの方法はすべて.Net 4.0で機能します。

1.XLLs

サードパーティベンダーのアドインエクスプレスはXLL機能を提供しますが、その無料で使いやすいExcel-DNAはここにありますhttps://stackoverflow.com/ users/44264

以下は、Excel-DNAページからの抜粋です。 https://Excel-dna.net/

はじめに

Excel-DNAは、.NETをExcelに統合するための独立したプロジェクトです。 Excel-DNAを使用すると、C#、Visual Basic.NET、またはF#を使用してExcelのネイティブ(.xll)アドインを作成し、高性能のユーザー定義関数(UDF)、カスタムリボンインターフェイスなどを提供できます。アドイン全体を、インストールや登録を必要としない単一の.xllファイルにパックできます。

はじめに

NuGetパッケージマネージャー(Windowsデスクトップ用Visual Studio 2012 Expressを含む)をサポートするVisual Studioのバージョンを使用している場合、Excel-DNAアドインを作成する最も簡単な方法は次のとおりです。

Visual Basic、C#、またはF#で新しいクラスライブラリプロジェクトを作成します。 NuGetパッケージの管理ダイアログまたはパッケージマネージャーコンソールを使用して、Excel-DNAパッケージをインストールします。

PM> Install-Package Excel-DNA

コードを追加します(C#、Visual Basic.NETまたはF#):

using ExcelDna.Integration;
public static class MyFunctions
{
    [ExcelFunction(Description = "My first .NET function")]
    public static string SayHello(string name)
    {
        return "Hello " + name;
    }
}

Excelで関数をコンパイル、ロード、使用します。

=SayHello("World!")

2.Automation AddIns

Eric Carterによるこの記事は、その方法を示しています。記事には画像の山が欠けているため、記事全体をコピー/貼り付け、保存用に画像を再作成しました。

参照: https://blogs.msdn.Microsoft.com/eric_carter/2004/12/01/writing-user-defined-functions-for-Excel-in-net/

Excelでは、Excelの数式で使用できるユーザー定義関数を作成できます。開発者は、XLLと呼ばれる特別な種類のDLLを作成する必要があります。Excelでは、Excelの数式で使用できるVBAでカスタム関数を作成することもできます。マネージコードを使用するXLL:現在または将来のバージョンのExcelでXLLが実行されない可能性がある場合、このシナリオを有効にするソリューションがあります。Webで「マネージXLL」を検索してください。

幸いなことに、XLL dllを作成する必要のないユーザー定義関数を作成する簡単な方法があります。 Excel XP、Excel 2003、およびExcel 2007は、オートメーションアドインと呼ばれるものをサポートしています。オートメーションアドインは、C#またはVB.NETで非常に簡単に作成できます。 C#の例を紹介します。

まず、Visual Studioを起動して、この例のAutomationAddinという新しいC#クラスライブラリプロジェクトを作成します。

次に、Class1.csファイルに、以下に示すコードを入力します。 GUID=独自のGUIDを使用して、Visual Studioの[ツール]メニューの[Generate GUID.

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace AutomationAddin
{

  // Replace the Guid below with your own guid that
  // you generate using Create GUID from the Tools menu
  [Guid("A33BF1F2-483F-48F9-8A2D-4DA68C53C13B")] 
  [ClassInterface(ClassInterfaceType.AutoDual)]
  [ComVisible(true)]
  public class MyFunctions
  {
    public MyFunctions()
    {

    }

    public double MultiplyNTimes(double number1, double number2, double timesToMultiply)
    {
      double result = number1;
      for (double i = 0; i < timesToMultiply; i++)
      {
        result = result * number2;
      }
      return result;
    }

    [ComRegisterFunctionAttribute]
    public static void RegisterFunction(Type type)
    {
      Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable"));
      RegistryKey key = Registry.ClassesRoot.OpenSubKey(GetSubKeyName(type, "InprocServer32"), true);
      key.SetValue("", System.Environment.SystemDirectory + @"\mscoree.dll",RegistryValueKind.String);
    }

    [ComUnregisterFunctionAttribute]
    public static void UnregisterFunction(Type type)
    {
      Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type, "Programmable"), false);
    }

    private static string GetSubKeyName(Type type, string subKeyName)
    {
      System.Text.StringBuilder s = new System.Text.StringBuilder();
      s.Append(@"CLSID\{");
      s.Append(type.GUID.ToString().ToUpper());
      s.Append(@"}\");
      s.Append(subKeyName);
      return s.ToString();
    }  
  }
}

このコードを記述したら、ソリューションエクスプローラーでプロジェクトの下のプロパティノードをダブルクリックして、プロジェクトのプロパティを表示します。 [ビルド]タブをクリックし、[COM相互運用機能に登録]というチェックボックスをオンにします。 Windows Vista以降で実行している場合、この時点で追加の手順があります。 COM相互運用機能に登録するには、Visual Studioを管理者権限で実行する必要があります。プロジェクトを保存し、Visual Studioを終了します。次に、[スタート]メニューでVisual Studioを見つけて右クリックし、[管理者として実行]を選択します。 Visual Studioでプロジェクトを再度開きます。次に、「ビルド」を選択してアドインをビルドします。

enter image description here

次の手順に従って、Excelを起動し、オートメーションサーバーダイアログに移動します。

  1. Excelを起動し、ウィンドウの左上隅にある[Microsoft Office]ボタンをクリックします。

  2. Excelオプションを選択します。

  3. [Excelオプション]ダイアログの[アドイン]タブをクリックします。

  4. [管理]というコンボボックスから[Excelアドイン]を選択します。次に、[Go]ボタンをクリックします。

  5. [アドイン]ダイアログの[オートメーション]ボタンをクリックします。

オートメーションアドインのリストでAutomationAddin.MyFunctionsを検索すると、作成したクラスを見つけることができます。

enter image description here

では、Excel内でMultiplyNTimes関数を使用してみましょう。最初に、番号、1番目の倍数の2番目の数、および1番目の数に2番目の数を掛ける回数を表す3番目の数を持つ単純なスプレッドシートを作成します。スプレッドシートの例を次に示します。

enter image description here

ブックの下の数字の下にある空のセルをクリックしてから、数式バーの[関数の挿入]ボタンをクリックします。利用可能な数式のダイアログから、「またはカテゴリを選択」ドロップダウンボックスをドロップダウンし、「AutomationAddin.MyFunctions」を選択します。

enter image description here

次に示すように、MultiplyNTimes関数をクリックします。

enter image description here

[OK]ボタンを押すと、Excelがダイアログをポップアップ表示し、次のようにスプレッドシートから関数の引数を取得できるようにします。

enter image description here

最後に、[OK]をクリックして、セルC3のカスタム数式とともにここに示す最終的なスプレッドシートを表示します。

enter image description here


3. Excel VBAから.Netを呼び出す

REF: vbaから.netライブラリメソッドを呼び出す

Automation.AddInプロジェクトのコードを使用して、Excel VBAからMultiplyNTimes関数を簡単に呼び出すことができます。

まず、ExcelからDLLへの参照を追加します。これを行うには、VBエディター。Alt+ F11を押して、[ツール]メニューをクリックします。および参照:

enter image description here

AutomationAddIn DLLを選択します。

enter image description here

VBAコードを追加して、.Net DLLを呼び出します。

Sub Test()

Dim dotNetClass As AutomationAddIn.MyFunctions
Set dotNetClass = New AutomationAddIn.MyFunctions

Dim dbl As Double
dbl = dotNetClass.MultiplyNTimes(3, 2, 5)

End Sub

そしてちょっとプレスト!

enter image description here


最後に、「Andrew Whitechapel」によるExcelおよび.Netに関するいくつかの優れたMSDN記事があります- google them

20
Jeremy Thompson

Soraco Technologiesのご厚意により、32ビットおよび64ビットの.NET 2.0および.NET 4.0でテストされたソリューションをご紹介します。

以下に提案するソリューションは遅延バインディングを使用し、.NETアセンブリの登録を必要としません。

宣言

次の宣言をプロジェクトに追加します。

#If VBA7 Then
Private Declare PtrSafe Function GetShortPathName Lib “Kernel32.dll” Alias “GetShortPathNameW” (ByVal LongPath As LongPtr, ByVal ShortPath As LongPtr, ByVal Size As Long) As Long
Private Declare PtrSafe Function SetDllDirectory Lib “Kernel32.dll” Alias “SetDllDirectoryW” (ByVal Path As LongPtr) As Long
Private Declare PtrSafe Sub LoadClr_x64 Lib “QlmCLRHost_x64.dll” (ByVal clrVersion As String, ByVal verbose As Boolean, ByRef CorRuntimeHost As IUnknown)
Private Declare PtrSafe Sub LoadClr_x86 Lib “QlmCLRHost_x86.dll” (ByVal clrVersion As String, ByVal verbose As Boolean, ByRef CorRuntimeHost As IUnknown)
#Else
Private Declare Function GetShortPathName Lib “Kernel32.dll” Alias “GetShortPathNameW” (ByVal LongPath As Long, ByVal ShortPath As Long, ByVal Size As Long) As Long
Private Declare Function SetDllDirectory Lib “Kernel32.dll” Alias “SetDllDirectoryW” (ByVal Path As Long) As Long
Private Declare Sub LoadClr_x64 Lib “QlmCLRHost_x64.dll” (ByVal clrVersion As String, ByVal verbose As Boolean, ByRef CorRuntimeHost As IUnknown)
Private Declare Sub LoadClr_x86 Lib “QlmCLRHost_x86.dll” (ByVal clrVersion As String, ByVal verbose As Boolean, ByRef CorRuntimeHost As IUnknown)
#End If ‘ WinAPI Declarations

' Declare variables
Dim m_myobject As Object
Dim m_homeDir As String

初期化

.NETアセンブリが配置されているパスにm_homeDir変数を初期化する必要があります。

たとえば、ExcelまたはMS-Accessファイルと同じフォルダーに.NETアセンブリをインストールする場合、m_homeDirを次のように初期化する必要があります。

Excel:m_homeDir = ThisWorkbook.Path

アクセス:m_homeDir = CurrentProject.Path

。NETオブジェクトの作成

次のコードをプロジェクトに追加します。

Private Function GetMyObject(dllPath As String, dllClass As String) As Object
    Dim LongPath As String
    Dim ShortPath As String

    LongPath = “\\?\” & m_homeDir
    ShortPath = String$(260, vbNull)

    PathLength = GetShortPathName(StrPtr(LongPath), StrPtr(ShortPath), 260)
    ShortPath = Mid$(ShortPath, 5, CLng(PathLength – 4))

    Call SetDllDirectory(StrPtr(ShortPath))
    Dim clr As mscoree.CorRuntimeHost

    If Is64BitApp() Then
        Call LoadClr_x64(“v4.0”, False, clr)
    Else
        Call LoadClr_x86(“v4.0”, False, clr)
    End If

    Call clr.Start

    Dim domain As mscorlib.AppDomain
    Call clr.GetDefaultDomain(domain)

    Dim myInstanceOfDotNetClass As Object
    Dim handle As mscorlib.ObjectHandle

    Set handle = domain.CreateInstanceFrom(dllPath, dllClass)

    Dim clrObject As Object
    Set GetMyObject = handle.Unwrap

    Call clr.Stop
End Function

Private Function Is64BitApp() As Boolean

    #If Win64 Then
        Is64BitApp = True
    #End If
End Function

.NETオブジェクトのインスタンス化

これで、.NETオブジェクトをインスタンス化し、使用を開始する準備が整いました。次のコードをアプリケーションに追加します。

m_homeDir = ThisWorkbook.Path 

m_myobject = GetMyObject(m_homeDir & “\yourdotnet.dll”, “namespace.class”)

最初の引数は、.NET DLLへのフルパスです。

2番目の引数は、Type.FullNameプロパティによって返される、アセンブリではなく名前空間を含む、要求された型の完全修飾名です。

必須DLL

このソリューションでは、.NET CLRをホストする2つのDLLを展開する必要があります。 DLLは、ExcelまたはMS-Accessファイルと同じフォルダーに展開されることが期待されています。

DLLはSoracoのWebサイトからダウンロードできます: https://soraco.co/products/qlm/QLMCLRHost.Zip

LGPL-2.1のライセンス供与

アプリケーションが Quick License Manager と直接または間接的に競合しない限り、DLLを使用する権利をここに付与します。これらのDLLは、商用または非商用アプリケーションで使用できます。

7
sam.porter

これが単なる偶然なのか、関連する質問を投稿したのかはわかりません。 SOはあなたの質問を示してくれました。何か貢献できると思います。

VBAとDLLを使用する場合、これまで見たほとんどの解決策は、DLLを登録し、com/gacを表示するように指示することです。ただし、VBAアプリケーションを配布している場合は、システムにDLLをインストールすることは望ましくありません。権限がない場合や、インストール/アンインストールプロセスを実行したり、参照の問題を処理したくない場合があります。

ただし、一部のWindows APIを使用してDLLを動的にロードできます。

[〜#〜] dll [〜#〜]

今の質問は、VBAから.NET DLLにアクセスする方法ですか?クライアントにOSアーキテクチャx86 x64が混在している場合、それに応じてこれを処理する必要があります。 32ビットのoffice/Excelで作業していると仮定しましょう。

.NET dllを作成し、VBAからアクセスしたい場合、「dllエントリポイントが見つかりません」のようなエラーメッセージがスローされます。ありがたいことに Robert Gieseckeabstract wrapper を作成しました。これにより、VBA経由で単純なDLL消耗品を作成できます。

テンプレート はここにあります。

しなければならないことはすべて

  1. Visual Studioで新しいクラスプロジェクトを作成する
  2. プロジェクトプラットフォームを32ビットのx86またはそれ以外のいずれかに設定します
  3. メインクラス内にメソッドを作成します。
  4. メインクラスをオブジェクトとして返す別のクラスを作成します(vbaに戻ります)
  5. (彼のウェブサイトからテンプレートに従ってください)

あなたが彼のテンプレートに従って、次のようなテストメソッドを作成したと仮定しましょう。

[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)]
public class YOUR_MAIN_CLASS
{
    [return: MarshalAs(UnmanagedType.BStr)]
    public string FN_RETURN_TEXT(string iMsg)
    {

        return "You have sent me: " + iMsg + "...";
    }
}

およびunmanagedexportクラス:

static class UnmanagedExports
{
    [DllExport]
    [return: MarshalAs(UnmanagedType.IDispatch)]
    static object YOUR_DLL_OBJECT()
    {
        return new YOUR_MAIN_CLASS();
    }
}

vba側からdllにアクセスする準備

ルートフォルダーにDLLを追加します。

#If VBA7 Then 
    Public Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
    Public Declare PtrSafe Function YOUR_DLL_OBJECT Lib "YOUR_DLL.dll" () As Object
#Else
    Public Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal strFilePath As String) As Long
    Public Declare Function YOUR_DLL_OBJECT Lib "YOUR_DLL.dll" () As Object
#End If

これで、dllをロードし、vbaでオブジェクトを作成およびアクセスすることができます。それは:

LoadLibrary (FN_APP_GET_BASE_PATH & "YOUR_DLL.dll")
dim mObj as object
set mObj = YOUR_DLL_OBJECT()
debug.print mObj.FN_RETURN_TEXT("Testing ..")

出力は

"You have sent me: Testing ....."

利点私は個人的にdllのインストールと参照が好きではありません。上記のテンプレートに従うことにより、何も参照する必要がなく、ロードしてインストールするだけで、DLLを自由に使用できます。

[〜#〜] note [〜#〜]:dll/.netコードはあなたのものであると仮定し、上記のテンプレートで再度コンパイルして。

上記のテンプレートで成功し、vbaの.NET非ブロック通知を作成しました。こちらをご覧ください: Microsoft Access(VBA)の通知のような非ブロック "トースト"

4
krish KM