2016-05-06 2 views
16

Я нашел способ вызова .NET 2 кода непосредственно из макроса VBA:Как вызывать методы .NET из Excel VBA

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 

(Для того, чтобы сделать этот код работы I шляпу, чтобы добавить ссылки на mscoree. tlb и mscorlib.tlb в Excel VBA с помощью инструментов -> Ссылки .. в Excel)

Но это работает только для сборки .NET CLR 2, вплоть до .NET framework версии 3.5.

Теперь мне нужно, чтобы заставить его работать с .NET 4.

я понял, что .NET CLR4 представила другой вариант агностик, способ создания экземпляра среды выполнения, и я также нашел довольно легко Пример кода, написанный на C++: http://dev.widemeadows.de/2014/02/04/hosting-the-net-4-runtime-in-a-native-process/

Но навыков моего VBA для Excel недостаточно, чтобы перевести эти несколько строк кода в рабочий макрос VBA. Может ли кто-нибудь мне помочь?

+1

Установили ли вы [Visual Studio Tools for Office Runtime] (https://msdn.microsoft.com/en-us/library/ee712596.aspx)? – Tim

+0

TLB все еще существуют с CLR4 (в C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319). в чем именно проблема?: –

+0

Я считаю, что это связано с регистрацией dll. Взглянуть. Надеюсь, поможет. http://www.geeksengine.com/article/register-dll.html – sam

ответ

15

Вот ответ canonical на 3 основных метода вызова .Net из Excel (или VBA).

Все три способа работы в .Net 4.0.

1. XLLs

3-й поставщик партия Надстройка Экспресс функциональность предложение XLL, однако его легко и свободно использовать Excel-ДНК автор здесьhttps://stackoverflow.com/users/44264

Вот выдержка со страницы Excel-ДНК: https://excel-dna.net/

Введение

Excel-DNA - это независимый проект по интеграции .NET в Excel. С помощью Excel-DNA вы можете создавать собственные (.xll) надстройки для Excel с помощью C#, Visual Basic.NET или F #, предоставляя высокопроизводительные пользовательские функции (UDF), настраиваемые ленточные интерфейсы и многое другое. Вся ваша надстройка может быть упакована в один .xll-файл, не требующий установки или регистрации.

Начало работы

Если вы используете версию Visual Studio, который поддерживает менеджер NuGet пакетов (включая Visual Studio 2012 Express, для рабочего стола Windows), самый простой способ сделать Excel-ДНК надстройка is to:

Создайте новый проект библиотеки классов в Visual Basic, C# или F #. Используйте Управление NuGet пакетов диалоговой или Package Manager Console для установки пакета Excel-ДНК:

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; 
    } 
} 

Compile, загружать и использовать функцию в Excel:

=SayHello("World!") 

2. автоматизации AddIns

Эта статья Эрик Картер показывает, как d o это, в статье отсутствуют кучи изображений, поэтому я копирую/вставляя всю статью и воссоздавая изображения для сохранения.

REF: https://blogs.msdn.microsoft.com/eric_carter/2004/12/01/writing-user-defined-functions-for-excel-in-net/

Excel позволяет создавать определяемые пользователем функции, которые могут быть использованы в формулах Excel. Разработчик должен создать специальный вид DLL, называемый XLL. Excel также позволяет писать пользовательские функции в VBA, которые можно использовать в формулах Excel. К сожалению, Excel не поддерживает или не рекомендует писать XLL, который использует управляемый код. Если вы готовы принять ваши шансы на то, что ваш XLL не будет работать в текущих или будущих версиях Excel, есть доступные решения, которые позволяют этот сценарий искать в Интернете «управляемый XLL».

К счастью, существует более простой способ создания пользовательской функции, которая не требует создания библиотеки XLL. Excel XP, Excel 2003 и Excel 2007 поддерживают что-то, называемое надстройкой автоматизации. Надстройка автоматизации может быть создана довольно просто на C# или VB.NET. Я приведу вам пример на C#.

Сначала запустите Visual Studio и создайте новый проект библиотеки классов C# под названием AutomationAddin для этого примера.

Затем в файле Class1.cs введите код, показанный ниже. Замените GUID своим собственным GUID, который вы создаете, используя Generate GUID в меню «Инструменты» Visual Studio.

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 или выше. Visual Studio должен запускаться с правами администратора для регистрации для COM-взаимодействия. Сохраните проект и выйдите из Visual Studio. Затем найдите Visual Studio в меню «Пуск» и щелкните правой кнопкой мыши по нему и выберите «Запуск от имени администратора». Повторно запустите проект в Visual Studio. Затем выберите «Build», чтобы создать надстройку.

enter image description here

Теперь запустите Excel и попасть в диалог серверов автоматизации, выполнив следующие действия:

  1. Запуск Excel и нажмите кнопку Microsoft Office в левом верхнем углу окна.

  2. Выберите опции Excel.

  3. Перейдите на вкладку надстроек в диалоговом окне «Параметры Excel».

  4. Выберите «Надстройки Excel» из поля со списком «Управление». Затем нажмите кнопку «Перейти».

  5. Нажмите кнопку «Автоматизация» в диалоговом окне «Надстройки».

Вы можете найти класс, созданный с помощью поиска AutomationAddin.MyFunctions в списке автоматизации надстроек:

enter image description here

Теперь давайте попробуем использовать функция MultiplyNTimes внутри Excel. Сначала создайте простую таблицу, в которой есть число, второе число и несколько первого, а третье число - сколько раз вы хотите умножить первое число на второе число. Пример таблицы показано здесь:

enter image description here

Нажмите на пустую ячейку, в книге под номерами, а затем нажмите на кнопку Insert Function в строке формул. В диалоговом окне доступных формул снимите раскрывающийся список «Или выберите категорию» и выберите «AutomationAddin.MyFunctions.

enter image description here

Затем нажмите на функцию MultiplyNTimes, как показано здесь:

enter image description here

При нажатии на кнопку OK, Excel всплывает диалоговое окно, чтобы помочь вы извлекаете аргументы функции из таблицы, как показано здесь:

enter image description here

Наконец, нажмите кнопку OK, чтобы увидеть окончательную таблицу, как показано здесь с пользовательской формулой в ячейке C3.

enter image description here


3. Вызов .Net из Excel VBA

REF: Calling a .net library method from vba

Использование кода из проекта Automation.AddIn можно легко вызвать функцию MultiplyNTimes из Excel VBA.

Сначала добавьте ссылку на DLL из Excel, для этого вам нужно будет находиться в редакторе 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


Наконец, есть несколько отличных статей MSDN о Excel и .Net на «Эндрю Whitechapel» - google them

+0

Я очень ценю усилия для этого ответа, но он не включает метод, который работает так же, как показывает оригинальный вопрос. Хотя в этом вопросе явно не указано это, основная причина использования этого метода заключается в том, что вы можете вызвать сборку, не регистрируя ее в VBA в качестве ссылки. Все методы, которые вы показываете, требуют, чтобы DLL была зарегистрирована и доступна в качестве ссылки. –

+0

Сейчас 12:54 здесь Джон позвольте мне вернуться к вам завтра, Thxs –

+0

Я пропустил то, что вы хотели сделать это без ссылки, вы все равно можете [делать это без прав администратора] (http://stackoverflow.com/questions/ 37193356/register-net-com-dlls-without-admin-rights-regasm), но, к сожалению, нужен regasm. У меня был отличный опыт и отличные проекты с использованием Excel-DNA. Убивая себя, я забыл упомянуть о работе Роберта Гизеке в этом ответе. –

3

Я не уверен, было ли это просто совпадение или потому, что я отправил относящийся вопрос. SO показал мне ваш вопрос, и я думаю, что мог бы что-то внести.

При работе с VBA и DLL большинство решений, которые я видел до сих пор, говорят мне, чтобы я зарегистрировал DLL и сделал его com/gac видимым. Если вы делаете это на своем ПК, это абсолютно нормально, но если вы распространяете приложение VBA, вы действительно не хотите устанавливать DLL в свою систему. У вас может не быть разрешения или вы действительно не хотите проходить процесс установки/удаления или возиться с проблемами ссылок.

Однако вы можете динамически загружать dlls, используя некоторые API окон.

DLL

Теперь вопрос заключается в том, чтобы получить доступ к DLL .NET из VBA? если ваши клиенты имеют смешанную архитектуру os x86 x64, вам необходимо соответствующим образом обрабатывать ее. Предположим, мы работаем над 32-разрядным офисом/Excel.

Если вы создаете dll .NET и хотите получить к нему доступ из VBA, он выдает сообщение об ошибке, подобное «Невозможно найти точку входа dll». к счастью, Robert Giesecke создал abstract wrapper, который позволит вам создавать простые DLL-расходные материалы через VBA.

A template можно найти здесь.

Все, что вам нужно сделать

  1. Создать новый проект класса в визуальной студии
  2. Установите платформу проекта либо x86 для 32-х и в противном случае
  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(); 
    } 
} 

Подготовка доступа к DLL из стороны Vba

Добавьте 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"() 
#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 с полной свободой.

ПРИМЕЧАНИЕ:: Я предполагаю, что код dll/.net является вашим, и вы можете скомпилировать его снова с помощью вышеуказанных шаблонов.

Я имел успех выше шаблона и создал .NET неблокирующая уведомления для VBA вы можете посмотреть здесь: non-blocking "toast" like notifications for Microsoft Access (VBA)

4

Вот ваше решение, тестирование для .NET 2.0 и .NET 4.0, 32 бит и 64 бит, любезно предоставлены Soraco Technologies.

Предлагаемое ниже решение использует позднюю привязку и не требует регистрации сборок .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 

Initialization

Вы должны инициализировать переменную m_homeDir на путь, где сборки .NET расположены.

Например, если установить сборки .NET в той же папке, что и файлы Excel или MS-Access, вы должны инициализировать 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.

Второй аргумент - это полное имя запрашиваемого типа, включая пространство имен, но не сборку, как было возвращено свойством Type.FullName.

Необходимые библиотеки DLL

Решение требует развертывания 2 библиотек DLL, которые отвечают за организацию .NET CLR. Ожидается, что библиотеки DLL будут развернуты в той же папке, что и файл Excel или MS-Access.

Библиотеки DLL можно загрузить с веб-сайта Soraco в: https://soraco.co/products/qlm/QLMCLRHost.zip

Лицензирование LGPL-2,1

Настоящим мы предоставляем вам право использовать наши библиотеки DLL, пока приложение не конкурирует непосредственно или косвенно с Quick License Manager. Вы можете использовать эти библиотеки DLL в своих коммерческих или некоммерческих приложениях.

+0

Лицо, разместившее этот вопрос, можете ли вы дать некоторые отзывы о предлагаемом нами решении? –

+0

Я подарил тебе, потому что ты не заслужил нисходящего. Если вы хотите привлечь кого-то внимание, как OP в этом случае, вы просто помещаете символ @ infront своего имени в комментарий, например @ sam.porter - удачи! –

0

политик по умолчанию препятствует CLR 4 из excuting унаследованного кода из 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> 

Или вы можете назвать родную функцию CorBindToRuntimeEx вместо New mscoree.CorRuntimeHost:

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 
Смежные вопросы