Есть похожие вопросы о передаче массивов параметров, и они работают в управляемом коде. При работе с Microsoft.Office.Interop.Excel
вы можете запустить макрос в файле Excel с помощью метода Application.Run
. Подпись метода показывает, что он принимает имя макроса и произвольный список аргументов, которые передаются в процедуру Excel VBA. Это выглядит и ведет себя как массив параметров, но это не так.
Когда вы передаете массив параметров в раздел аргументов .Run
, вы получаете:
System.Runtime.InteropServices.COMException: «Необязательный параметр. (Исключение из HRESULT: 0x8002000F (DISP_E_PARAMNOTOPTIONAL))'
Это говорит о том, что массив параметров передается неправильно, вероятно, из-за того, что COM-взаимодействие не имеет типа ParamArray
.
Как мне расширить мою произвольную длину ParamArray
в произвольный список аргументов, которые можно передать COM-объекту?
ПРИМЕР КОДА:
''' <summary>
''' Run an Excel macro silently in the background. Quits at the end.
''' </summary>
''' <param name = "excelFileName">Full path to Excel file.</param>
''' <param name = "macroName">The macro you want to run.</param>
''' <param name = "args">Arguments to pass into that macro procedure.</param>
Public Shared Sub RunMacro(excelFileName As String, macroName As String, ParamArray args As String())
' Create new Excel instance
Dim excelApp = New Application With {
.Visible = True,
.EnableEvents = False ' Suppress the Workbook_Open event
}
Dim excelBooks As Workbooks = excelApp.Workbooks
' Open the Excel workbook
Dim thisWorkbook As Workbook = excelBooks.Open(excelFileName)
' Hide the window while the macro runs
excelApp.Visible = False
' Run the VBA procedure.
excelApp.Run(macroName, args) ' <-- Throws because it can't accept ParamArray
' Cleanup
thisWorkbook.Close(SaveChanges:=False)
excelApp.Quit()
End Sub
Как насчет одного из решений здесь? stackoverflow.com/questions/20783170/pass-array-to-paramarray Не знаю, как/переводятся ли они на .Net...
@TimWilliams, я видел это раньше. Это для распаковки, а не для перехода к COM.
Я не могу понять, чего вы пытаетесь достичь... Если вы попытаетесь вызвать Sub
/Function
с именем macroName
, имеющим один параметр ParamArray()
, вы не сможете вызвать его, передав массив. Даже если вы псевдо объявите это как ParamArray()
. Не только через COM-взаимодействие. Вы не можете сделать это и в Excel VBA. В VBA ParamArray()
используется только для передачи неопределенного количества параметров/типов. Но вы должны вызывать функцию, используя все эти параметры!. Если вы хотите передать массив, сделайте это, но не объявляйте как 'ParamArray()` и не объявляйте в вызываемой подпрограмме arr () As Variant
.
В заключение, Excel (VBA, COM, что угодно) использует, допускает ParamArray()
только в качестве последнего параметра вызываемой подпрограммы/функции. И делает это только для того, чтобы разрешить неопределенное количество аргументов/типов при выполнении вызова, как указано выше... Пожалуйста, смотрите здесь...
@FaneDuru Чтобы вы поняли, что я делаю, вам нужно знать, что такое подпись Application.Run
. В документации говорится, что он может принимать от 0 до 30 аргументов, как и ParameterArray. learn.microsoft.com/en-us/office/vba/api/Excel.Application.Run
Да, это его определение. Но выше я предложил более простой способ, который выглядит непонятным... Если вы используете args As Variant
и больше не используете Sub macroName(ParamArray() As Variant)
. Если вы используете Sub macroName(arrP As Variant)
, вы можете передать весь массив args
настолько большим, насколько вам нужно, и обработать его в процедуре книги Excel, как хотите...
Я пытался написать обертку, которая соответствовала бы подписи Application.Run
, и в конце концов мне это удалось. Я думаю, что то, что вы предлагаете, требует модификации кода VBA, где то, что я делаю, будет работать с любым макросом Excel.
В VBA вы бы назвали это Application Run "WorkbookName!macroName", args
. Преобразуйте и используйте его, вызывая через COM, и он будет работать. Теперь мне нужно закрыть свой ноутбук.
Ответ, который я нашел, часто бывает, когда сталкиваешься с невозможной ситуацией. «Не делай этого».
Метод взаимодействия .Run
не принимает ParameterArray
, и нет возможности динамически передавать аргументы. Значит, нам нужен другой метод. В случае COM-взаимодействия вы можете вручную .InvokeMember
принимать параметры в виде массива объектов. Это позволяет нам обойти тот факт, что .Run
принимает одну обязательную строку и до 30 аргументов, что позволяет нам передавать их все сразу в виде массива.
Окончательный метод выглядит так:
''' <summary>
''' Run an Excel macro silently in the background. Quits at the end.
''' </summary>
''' <param name = "excelFileName">Full path to Excel file.</param>
''' <param name = "macroName">The macro you want to run.</param>
''' <param name = "args">Arguments to pass into that macro procedure.</param>
Public Shared Sub RunMacro(excelFileName As String, macroName As String, ParamArray args As String())
' Make an array of object of string for the invoker
Dim argumentArray = args.ToArray()
Dim objectArray = New Object(argumentArray.Length) {}
objectArray(0) = macroName
If argumentArray IsNot Nothing Then argumentArray.CopyTo(objectArray, 1)
Dim excelApp As Application = Nothing
Dim excelBooks As Workbooks = Nothing
Dim thisWorkbook As Workbook = Nothing
Try
' Create new Excel instance
excelApp = New Application With {
.Visible = False, ' Hide the window while the macro runs
.EnableEvents = False ' Suppress the Workbook_Open event
}
' Open the Excel workbook
excelBooks = excelApp.Workbooks
thisWorkbook = excelBooks.Open(excelFileName)
excelApp.GetType().InvokeMember(
"Run", Reflection.BindingFlags.Default Or Reflection.BindingFlags.InvokeMethod, Nothing, excelApp, objectArray
)
Finally ' Cleanup
If thisWorkbook IsNot Nothing Then
thisWorkbook.Close(SaveChanges:=False)
Marshal.ReleaseComObject(thisWorkbook)
End If
If excelBooks IsNot Nothing Then Marshal.ReleaseComObject(excelBooks)
If excelApp IsNot Nothing Then
excelApp.Quit()
Marshal.ReleaseComObject(excelApp)
End If
End Try
End Sub
Это вопрос, который я поднимал в обсуждениях TwinBasic. VBA не может пересылать ParamArray как список элементов. Чтобы удовлетворить потребности запуска приложения, вам нужно будет разобрать массив параметров.