среда, 11 сентября 2013 г.

VBA: как выполнить скрипт Perl и/или любого другого командного интерпретатора

Каждый язык программирования хорош в своей нише. Вызов программ, написанных на одном языке, из программ на другом языке позволяет быстрее и проще решать некоторые задачи, чем программировать решение с использованием единственного языка.

Рассмотрим наиболее удобный (для меня) способ выполнения программ на языке Perl из программ, написанных на VBA.

Я не затрагиваю такие вопросы, как создание нового макроса и/или модуля для VBA-приложений. Подразумевается, что читатель знаком.

Для работы потребуется ссылка на Microsoft Scripting Runtime, которая ставится в диалоговом окне Tools > References...:


Далее пишем код:
#If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
#Else
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

Private Type RunResult
    ExitCode As Integer
    StdOut As String
    StdErr As String
    StdIn As String
    ProcessID As Integer
End Type

Function RunProgram(sProgram As String, Optional sParams As String = "", _
    Optional sCurrentDir As String = "", Optional sStdIn As String = "") As RunResult
    Dim oShell As Object
    Set oShell = CreateObject("WScript.Shell")
    If sCurrentDir <> "" Then
        oShell.CurrentDirectory = sCurrentDir
    End If
    
    Dim sCmd As String
    sCmd = sProgram & " " & sParams

    Dim oExec As Object
    Set oExec = oShell.Exec(sCmd)
    ' Если есть данные стандартного потока ввода, то передаём их программе
    If sStdIn <> "" And oExec.Status <> 1 Then
        oExec.StdIn.WriteLine sStdIn
    End If
    oExec.StdIn.Close
    ' Ожидаем окончания программы
    Do While oExec.Status <> 1
        Sleep 500
    Loop
    
    RunProgram.ExitCode = oExec.ExitCode
    RunProgram.StdOut = oExec.StdOut.ReadAll()
    RunProgram.StdErr = oExec.StdErr.ReadAll()
    RunProgram.StdIn = sStdIn
    RunProgram.ProcessID = oExec.ProcessID
    
    Set oShell = Nothing
End Function

Sub RunPerl()
    Dim Результат As RunResult
    Результат = RunProgram("c:\perl\bin\perl.exe", sParams:="1.pl")
    Результат = RunProgram("c:\perl\bin\perl.exe", sParams:="""d:\Мой полигон\test.pl""", sStdIn:="12345")
    Результат = RunProgram("d:\Мой полигон\test.cmd", sParams:="111 222")
    MsgBox ("STDOUT " + Результат.StdOut)
    MsgBox ("STDERR " + Результат.StdErr)
End Sub

Вся работа выполняется в функции RunProgram, которая имеет следующие параметры:
  • sProgram - это путь к запускаемому файлу. Им может быть интерпретатор Perl, командный файл (*.cmd) и т.д. Как видно из примера, допускается использование символов пробела в пути и/или в имени файла.
  • sParams задаёт параметры для вызываемой программы. При вызове функции программист должен сам следить на разделение параметров пробелами.
  • sCurrentDir позволяет задать текущую рабочую папку при выполнении программы.
  • sStdIn позволяет задать содержимое стандартного потока ввода.

Результаты выполнения программы представлены в виде структуры RunResult со следующими полями:
  • ExitCode - хранит код завершения программы (программисты на C/C++ возвращают это значение с помощью return в main).
  • StdOut - содержимое стандартного потока вывода.
  • StdErr - содержимое стандартного потока ошибок.
  • StdIn - содержимое стандартного потока ввода.
  • ProcessID - идентификатор запущенного процесса.

В процедуре RunPerl показаны различные примеры вызова (2 из них закомментированы). Для Perl использовался следующий скрипт:
#!/usr/bin/perl
my $str = <STDIN>;
print STDOUT ">>STDOUT>>$str<<";
print STDERR ">>STDERR>>$str<<";
а для командного интерпретатора Windows этот:
@echo off
echo %%1=%1
echo %%2=%2

Программисты оценят захват содержимого потоков ввода-вывода, поскольку они чаще всего служат для передачи информации между скриптами.

Полезно почитать:
http://www.script-coding.com/WSH/WshShell.html (обзор свойств объекта WshShell)

Комментариев нет:

Отправить комментарий