Подтвердить что ты не робот

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

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

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

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

Declare Function getString Lib "<PATH of my DLL>" () as string

sub test()
   range("A1").value = getString
End Sub

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

4b9b3361

Ответ 1

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

Я повторно использую образец, который я создал недавно для другого потока 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

Ответ 2

У меня была эта проблема много раз. я закончил регистрацию 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

Надеюсь, это поможет:)

Ответ 3

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

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

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

Он работал btw!

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

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