Как я могу передать массив параметров COM-объекту?

Есть похожие вопросы о передаче массивов параметров, и они работают в управляемом коде. При работе с 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

Это вопрос, который я поднимал в обсуждениях TwinBasic. VBA не может пересылать ParamArray как список элементов. Чтобы удовлетворить потребности запуска приложения, вам нужно будет разобрать массив параметров.

freeflow 07.12.2022 18:50

Как насчет одного из решений здесь? stackoverflow.com/questions/20783170/pass-array-to-paramarra‌​y Не знаю, как/переводятся ли они на .Net...

Tim Williams 07.12.2022 19:59

@TimWilliams, я видел это раньше. Это для распаковки, а не для перехода к COM.

HackSlash 07.12.2022 20:08

Я не могу понять, чего вы пытаетесь достичь... Если вы попытаетесь вызвать Sub/Function с именем macroName, имеющим один параметр ParamArray(), вы не сможете вызвать его, передав массив. Даже если вы псевдо объявите это как ParamArray(). Не только через COM-взаимодействие. Вы не можете сделать это и в Excel VBA. В VBA ParamArray() используется только для передачи неопределенного количества параметров/типов. Но вы должны вызывать функцию, используя все эти параметры!. Если вы хотите передать массив, сделайте это, но не объявляйте как 'ParamArray()` и не объявляйте в вызываемой подпрограмме arr () As Variant.

FaneDuru 07.12.2022 21:39

В заключение, Excel (VBA, COM, что угодно) использует, допускает ParamArray() только в качестве последнего параметра вызываемой подпрограммы/функции. И делает это только для того, чтобы разрешить неопределенное количество аргументов/типов при выполнении вызова, как указано выше... Пожалуйста, смотрите здесь...

FaneDuru 07.12.2022 21:47

@FaneDuru Чтобы вы поняли, что я делаю, вам нужно знать, что такое подпись Application.Run. В документации говорится, что он может принимать от 0 до 30 аргументов, как и ParameterArray. learn.microsoft.com/en-us/office/vba/api/Excel.Application.R‌​un

HackSlash 07.12.2022 22:03

Да, это его определение. Но выше я предложил более простой способ, который выглядит непонятным... Если вы используете args As Variant и больше не используете Sub macroName(ParamArray() As Variant). Если вы используете Sub macroName(arrP As Variant), вы можете передать весь массив args настолько большим, насколько вам нужно, и обработать его в процедуре книги Excel, как хотите...

FaneDuru 07.12.2022 22:18

Я пытался написать обертку, которая соответствовала бы подписи Application.Run, и в конце концов мне это удалось. Я думаю, что то, что вы предлагаете, требует модификации кода VBA, где то, что я делаю, будет работать с любым макросом Excel.

HackSlash 07.12.2022 22:23

В VBA вы бы назвали это Application Run "WorkbookName!macroName", args. Преобразуйте и используйте его, вызывая через COM, и он будет работать. Теперь мне нужно закрыть свой ноутбук.

FaneDuru 07.12.2022 22:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
9
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ответ, который я нашел, часто бывает, когда сталкиваешься с невозможной ситуацией. «Не делай этого».

Метод взаимодействия .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

Другие вопросы по теме