Использование DLL C # внутри EXCEL VBA

Я столкнулся с небольшой проблемой здесь и нуждаюсь в помощи вам, ребята.

У меня есть DLL C #, открытая через COM-взаимодействие. Он работает нормально, но, по-видимому, развертывание объекта взаимодействия C # является катастрофой, и вам нужно регаз каждый раз, когда вы обновляете DLL.

Поэтому мне интересно, как я могу использовать функции из этой C # DLL следующим образом: или все, что я могу назвать функциями, просто поместив DLL и электронную таблицу вместе.

Declare Function getString Lib "" () as string sub test() range("A1").value = getString End Sub 

Синтаксис может быть неправильным.

Вы можете это сделать, но вы должны знать различия VBA и .Net.
Во-первых, вам нужно создать фактическую DLL (сборки .NET нет), чтобы сделать это, используйте этот шаблон проекта . Опять же, вы должны знать, как маршалировать вещи.
VBA поддерживает только stdcall как соглашение о вызове и не может действительно работать с функциями Unicode для DLL. Это не так плохо, поскольку по умолчанию маршалинг для String в .Net – это то, что ожидает VBA (указатель на Ansi char). Кроме того, stdcall – это соглашение по умолчанию, которое я использую для экспорта.

Я повторно использую образец, который я создал недавно для другого streamа SO:

Поместите это в проект, который вы создали с помощью моего шаблона:

 [ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)] public class Sample { public string Text { [return: MarshalAs(UnmanagedType.BStr)] get; [param: MarshalAs(UnmanagedType.BStr)] set; } [return: MarshalAs(UnmanagedType.BStr)] public string TestMethod() { return Text + "..."; } } static class UnmanagedExports { [DllExport] [return: MarshalAs(UnmanagedType.IDispatch)] static Object CreateDotNetObject(String text) { return new Sample { Text = text }; } } 

Вот как это называется VBA:

 Declare Function CreateDotNetObject Lib "The full path to your assembly or just the assembly if it is accessible from Excel" _ (ByVal text As String) As Object Sub test() Dim instance As Object Set instance = CreateDotNetObject("Test 1") Debug.Print instance.Text Debug.Print instance.TestMethod instance.text = "abc 123" ' case insensitivity in VBA works as expected' Debug.Print instance.Text End Sub 

Это используемые операторы, которые должны быть в верхней части вашего classа, которые являются ключевыми:

использование System.Diagnostics; используя RGiesecke.DllExport;

Также убедитесь, что у вас есть проект, запущенный до инструкции Nuget PM, чтобы установить шаблон выше. Я новичок в этом – я уверен, что есть и другие. Я использую AutoCAD VBA и получил еще одну ошибку, так как это 64 бит – мне пришлось использовать PtrSafe (и т. Д.) В заявлении Declare для VBA, чтобы продолжить без ошибок (см. MS docs для этого http://support.microsoft.com/kb / 983043 )

Это сработало кстати!

Мой последний код (на основе вышеизложенного)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; using RGiesecke.DllExport; namespace ClassLibrary3 { [ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)] public class Class1 { public string Text { [return: MarshalAs(UnmanagedType.BStr)] get; [param: MarshalAs(UnmanagedType.BStr)] set; } [return: MarshalAs(UnmanagedType.BStr)] public string TestMethod() { return Text + "..."; } } static class UnmanagedExports { [DllExport] [return: MarshalAs(UnmanagedType.IDispatch)] static Object CreateDotNetObject(String text) { return new Class1 { Text = text }; } } } 

и мой код vba:

 #If VBA7 Then Private Declare PtrSafe Function CreateDotNetObject Lib "G:\gitRepository\VS\ClassLibrary3\ClassLibrary3\bin\Debug\ClassLibrary3.dll" (ByVal text As String) As Object #Else Private Declare Function CreateDotNetObject Lib "G:\gitRepository\VS\ClassLibrary3\ClassLibrary3\bin\Debug\ClassLibrary3.dll" (ByVal text As String) As Object #End If Sub test() Dim instance As Object Set instance = CreateDotNetObject("Test 1") Debug.Print instance.text Debug.Print instance.TestMethod instance.text = "abc 123" ' case insensitivity in VBA works as expected' Debug.Print instance.text End Sub 

У меня была эта проблема много раз. я закончил регистрацию com dll из vba с помощью метода оболочки и ожидания, на regasm, чтобы зарегистрировать / отменить регистрацию dll до поздней привязки, создав объект com через

  CreateObject('yourclasshere') 

Его немного взломать, но он работает, использует метод shellandwait и метод регистрации и отмены регистрации.

  Private Declare Function OpenProcess Lib "kernel32" _ (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _ ByVal dwProcessId As Long) As Long Private Declare Function GetExitCodeProcess Lib "kernel32" _ (ByVal hProcess As Long, lpExitCode As Long) As Long Private Const STATUS_PENDING = &H103& Private Const PROCESS_QUERY_INFORMATION = &H400 Private Function ShellandWait(ExeFullPath As String, _ Optional TimeOutValue As Long = 0) As Boolean Dim lInst As Long Dim lStart As Long Dim lTimeToQuit As Long Dim sExeName As String Dim lProcessId As Long Dim lExitCode As Long Dim bPastMidnight As Boolean On Error GoTo ErrorHandler lStart = CLng(Timer) sExeName = ExeFullPath 'Deal with timeout being reset at Midnight If TimeOutValue > 0 Then If lStart + TimeOutValue < 86400 Then lTimeToQuit = lStart + TimeOutValue Else lTimeToQuit = (lStart - 86400) + TimeOutValue bPastMidnight = True End If End If lInst = Shell(sExeName, vbHide) lProcessId = OpenProcess(PROCESS_QUERY_INFORMATION, False, lInst) Do Call GetExitCodeProcess(lProcessId, lExitCode) DoEvents If TimeOutValue And Timer > lTimeToQuit Then If bPastMidnight Then If Timer < lStart Then Exit Do Else Exit Do End If End If Loop While lExitCode = STATUS_PENDING ShellandWait = True Exit Function ErrorHandler: ShellandWait = False End Function Private Function RegisterPayload() As Boolean Dim script As String script = "cmd /c" script = script + " " + "%windir%\Microsoft.NET\Framework\v2.0.50727\regasm" script = script + " " + Chr(34) + InstallationPath + Chr(34) script = script + " /codebase" RegisterPayload = ShellandWait(script) End Function Private Function UnRegisterPayload() As Boolean Dim script As String script = "cmd /c" script = script + " " + "%windir%\Microsoft.NET\Framework\v2.0.50727\regasm" script = script + " " + Chr(34) + InstallationPath + Chr(34) script = script + " /u" UnRegisterPayload = ShellandWait(script) End Function 

Надеюсь, поможет 🙂