2015-02-05 23 views
5

ho trascorso gran parte della giornata alla ricerca di una soluzione a questo, sto iniziando a pensare che la sua forse non è possibile per le mie esigenzereale tempo di uscita della console da WScript.Shell Exec

La mia configurazione di base è quello di eseguire un vbscript (.vbs) chiamato da un codice vba excel. Il codice VBA deve continuare e lasciare il vbscript in esecuzione, ma controllerà di tanto in tanto con Exec.Status

Nel vbscript sto usando WScript.StdOut.WriteLine "whatever" per rintracciare/debug è progresso, ma così com'è posso solo leggere viene emesso dopo che il codice vba excel è terminato, cosa deve fare.

Quello che voglio è quello di vedere una vera e propria uscita di tempo per la console dal VBScript

Ecco il codice VBA ...

Dim WSH As IWshRuntimeLibrary.WshShell 'Windows Script Host Object Model 
Dim Exec As WshExec 

Set WSH = CreateObject("WScript.Shell") 
Set Exec = WSH.Exec("%COMSPEC% /C CSCRIPT.EXE //nologo " _ 
    & VbsFileDir _ 
    & " " & Arg1 _ 
    & " " & Arg2 _ 
    & " " & Arg3 _ 
    & " " & Arg4) 

sono stato in grado di ottenere una produzione in tempo reale convertendo WSH.Exec-WSH.Run, ma ho bisogno l'accesso ai Exec.Status, che non è disponibile in WSH.Run


AGGIORNAMENTO - 2015-02-06

per chiarire ulteriormente ... Utilizzando l'esempio' ... B.vbs' codice fornito da @ di Ekkehard.Horner risposta ... Il seguente codice di Excel VBA visualizzare un'uscita in tempo reale alla console ...

WSH.Run("cscript C:\28353522-B.vbs") 

... ma quanto segue non visualizzerà nulla alla console

WSH.Exec("cscript C:\28353522-B.vbs") 

non posso usare il .Run() perché io uso il .Status bandiera da .Exec() Inoltre, non posso spostare il vbscript nel codice VBA perché VBA continua a svolgere altre attività in parallelo con vbscript.

P.s Se qualcuno può presentare una risposta che spieghi perché non può essere eseguita, la contrassegnerò come accettata.

risposta

3

Uso .Stdout.ReadLine() fino a quando il processo è terminato e .Stdout.ReadAll() per trangugiare il resto dell'output - come in

28.353.522-A.vbs

Option Explicit 

Const WshFinished = 1 

Dim oExc : Set oExc = CreateObject("WScript.Shell").Exec("cscript 28353522-B.vbs") 
WScript.Echo "A", "start" 
Do While True 
    If oExc.Status = WshFinished Then 
     WScript.Echo "A", "WshFinished" 
     Exit Do 
    End If 
    WScript.Sleep 500 
    If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadLine() 
Loop 
If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadAll() 

28353522 -B.vbs

Option Explicit 

Dim i 
For i = 1 To 10 
    WScript.Echo "B", i, "whatever" 
    WScript.Sleep 100 
Next 

uscita:

cscript 28353522-A.vbs 
A start 
A B 1 whatever 
A B 2 whatever 
A B 3 whatever 
A WshFinished 
A B 4 whatever 
B 5 whatever 
B 6 whatever 
B 7 whatever 
B 8 whatever 
B 9 whatever 
B 10 whatever 

BTW - Come hai ottenuto l'output in tempo reale con .Run()?

+0

I darò un suggerimento al tuo suggerimento stamattina e ti farò sapere Solo per assicurarmi di aver capito bene ... Il mio excel vba code eseguirà Exec the middle-man (A.vbs) che eseguirà il mio vbscript principale (B.vbs) ed Echo output. Ma, domanda ... Se il mio codice excel vba chiama l'A.vbs con lo stesso metodo con cui chiama già il B.vbs, allora perché l'A. vbs avrà una fortuna migliore? E non ho fatto niente di speciale per il .Run(), ho appena sostituito .Exec() e ha funzionato. – Skytunnel

+0

@Skytunnel - no, A è un falso per il tuo codice VBA. –

+0

Quindi ho bisogno di replicare il codice A riscritto come codice VBA? Il problema con questo è il mio codice VBA ha altro lavoro da fare, non posso averlo seduto a guardare il B.vbs. Ho cercato di spiegarlo sulla domanda. – Skytunnel

0

Perché stai eseguendo due file? Non c'è bisogno.

VBA, essendo di base completo, può scrivere sulla propria console.

Quindi metti i tuoi vbs in VBA (puoi tagliare e incollare VBS in VBA e funzionerà se metterai sub/end sub attorno ad esso).Per far funzionare il VBS in VBA, metti un timer che lo spara.

Ecco un modulo di classe per VBA per creare/leggere/scrivere console.

'User global var gconsole 
Public Function WriteToConsoles(sOut As String) 
    If IsConsoleAvailable() = True Then 
     Dim Result As Long, cWritten As Long 
     Result = WriteConsole(hConsole, ByVal sOut, Len(sOut), cWritten, ByVal 0&) 
    End If 
End Function 

Public Sub ExecuteCommand(Cmd As String, ReturnToPrompt As Boolean) 
    If IsConsoleAvailable() = True Then 
     If Len(Cmd) <> 0 Then 
      If ReturnToPrompt = True Then 
       Shell Environ$("comspec") & " /k " & Cmd 
      Else 
       Shell Environ$("comspec") & " /c " & Cmd 
      End If 
     End If 
    End If 
End Sub 

Public Sub CreateConsole() 
    If IsConsoleAvailable() = False Then 
     If AllocConsole() Then 
      hConsole = GetStdHandle(STD_OUTPUT_HANDLE) 
      If hConsole = 0 Then MsgBox "Couldn't allocate STDOUT" 
     Else 
      MsgBox "Couldn't allocate console" 
     End If 
    End If 
End Sub 

Public Sub CloseConsole() 
    If IsConsoleAvailable() = True Then 
     CloseHandle hConsole 
     hConsole = 0 
     FreeConsole 
    End If 
End Sub 

Public Function IsConsoleAvailable() As Boolean 
     If hConsole <> 0 Then 
      IsConsoleAvailable = True 
     Else 
      IsConsoleAvailable = False 
     End If 
End Function 
+0

Per essere onesti, ho semplificato la domanda ... In realtà è in esecuzione più di 2 file ... Effettivamente il codice VBA agisce come un controllore, facendo scattare più file VBS (quindi più lavoro in esecuzione in parallelo). Non voglio davvero entrare nei dettagli perché è troppo complesso per lo scopo principale del mio problema. Grazie per il suggerimento, di solito uso il {debug.print} di VBA per fungere da finestra della console, non ho mai visto il tuo metodo prima d'ora! Potrei provarlo qualche volta. Grazie! – Skytunnel

0

Ho trovato una risposta alla mia domanda. Anche se questa non è una soluzione preferita (come spiegherò di seguito), quindi non segnerò come corretta, ma forse qualcuno può risolvere i problemi con la soluzione? (in tal caso, inserisci una risposta e contrassegnerò come corretta)

Prima di tutto, +1 a @ Ekkehard.La risposta di Hanner per ispirare questa soluzione.

Creare il file "B.vbs" che rappresenta il mio vbscript principale da eseguire.

Option Explicit 
Dim i 
For i = 1 to 10 
    Wscript.Echo "B", i, "whatever" 
    Wscript.Sleep 100 
Next 

Creare il file 'A.vbs' di agire come un uomo di mezza tra il VBScript principale e il mio codice VBA di Excel

Option Explicit 
Dim WSH 
Set WSH = CreateObject("WScript.Shell") 
WSH.Run "cscript C:\B.vbs", , True 
Set WSH = Nothing 

Ora il codice VBA di Excel ...

Option Explicit 
Sub Test() 
    Dim WSH As IWshRuntimeLibrary.WshShell 
    Dim Exec As WshExec 
    Set WSH = CreateObject("WScript.Shell") 

    Set Exec = WSH.Exec("cscript C:\A.vbs") 

    'Showing that I can still access the Exec.Status 
    While Exec.Status = WshRunning 
     Debug.Print "Running" 
    Wend 
    'But downside is nothing is avaiable from Stdout 
    Debug.Print Exec.StdOut.ReadAll 

    Set Exec = Nothing 
    Set WSH = Nothing 

End Sub 

Quindi il VBA di Excel chiama "A.vbs" utilizzando ancora WSH.Exec(), quindi chiamerà "B.vbs" utilizzando WSH.Run(), che apre una seconda finestra della console che visualizzerà l'output in tempo reale

Vantaggi

  • Excel VBA può ancora controllare il Exec.Status accuratamente
  • Progressi 'B.vbs' possono essere visti da tempo reale output della console

Svantaggi (motivi per cui non contrassegno come corretto)

  • Il Exec.Terminate() terminerà solo il 'A.vbs' (prima finestra della console), il 'B.vbs' rimarrà in esecuzione
  • Il Exec.StdOut. non può leggere l'output di 'B.vbs'