2013-11-25 26 views
13

Sto chiamando una macro di Excel da uno script di regole di Outlook.VBA Outlook chiama Excel Macro e aspetta che la macro sia stata completata

Il processo è: Ottenere la posta, eseguire una regola di Outlook che esegue uno script di Outlook, aprire Excel da quello script, eseguire la macro di Excel, chiudere Excel.

Come posso verificare nello script di regole di Outlook che la macro di Excel è stata eseguita, per salvare e chiudere l'applicazione?

Sub AskMeAlerts() 
Dim appExcel As Excel.Application 
Dim wkb As Excel.Workbook 
Set appExcel = CreateObject("Excel.Application") 
appExcel.Workbooks.Open ("C:\Ask me question workflow.xlsm") 
appExcel.Visible = True 
appExcel.Run "'Ask me question workflow.xlsm'!AskMeFlow" 
appExcel.DisplayAlerts = False 
appExcel.ActiveWorkbook.Save 
appExcel.Quit Set appExcel = Nothing 
Set wkb = Nothing 
End Sub 
+0

prego ci mostra il relativo parti del tuo codice. (Cioè non il tutto) –

+0

'Dim appExcel Come Excel.Application Dim wkb Come Excel.Workbook Set appExcel = CreateObject ("Excel.Application") appExcel.Workbooks.Open ("C: \ Chiedimi in discussione del flusso di lavoro .xlsm ") appExcel.Visible = true appExcel.Run " 'Chiedimi dubbio workflow.xlsm'! AskMeFlow" ---- Qui voglio aspettare la macro è fatto ---- appExcel .DisplayAlerts = False appExcel.ActiveWorkbook.Save appExcel.Quit Imposta appExcel = Nulla Set wkb = Nothing End Sub ' – user3016795

+1

Immagino che tu possa vedere da solo che questo è completamente illeggibile ... Modifica la tua domanda, inserisci il tuo codice lì e formattalo correttamente usando il pulsante '{}'. –

risposta

4

Si potrebbe o

  1. Port macro di Excel in Outlook ed eseguirlo direttamente
  2. Utilizzare un bandiera per catturare il completamento del codice

Il seguente codice utilizza un marcatore in A1 del primo foglio per catturare il codice da eseguire (nella parte di Excel). Ho anche venivano pulite il codice (era un mix di precoce e un successivo binding)

codice prospettive

Sub AskMeAlerts() 
Dim appExcel As Excel.Application 
Set appExcel = New Excel.Application 
With appExcel 
    .DisplayAlerts = False 
    .Workbooks.Open ("C:\TEMP\Ask me question workflow.xlsm") 
    .Run "'Ask me question workflow.xlsm'!AskMeFlow" 
    If .activeworkbook.sheets(1).[a1].Value = "Complete" Then 
     MsgBox "Code has run" 
     .activeworkbook.sheets(1).[a1].Value = vbNullString 
     .activeworkbook.Save 
     .DisplayAlerts = True 
     .activeworkbook.Close 
     appExcel.Quit 
     Set appExcel = Nothing 
    End If 
End With 
End Sub 

eccellono codice

Sub AskMeFloW() 
'do stuff 
ThisWorkbook.Sheets(1).[a1] = "Complete" 
End Sub 
+0

Forse sto leggendo il tuo codice in modo errato, ma non la subroutine AskMeAlerts() valuterà semplicemente '.activeworkbook.sheets (1). [A1] .Value = "Completa" 'come Falso e poi salta il blocco If? Dovresti avere qualche tipo di attesa, un Do o While? –

+0

@NickPeranzi No. Prova a testare il codice come sopra (aggiungi un 'MsgBox .activeworkbook.sheets (1) .Range (" A1 "). Value' prima e dopo la riga .Run). – brettdj

+0

Quindi, anche la "bandiera" importa? Poiché non esiste alcuna attesa tra la macro di Outlook che esegue l'istruzione .Run e controlla il valore, significa che la macro di Outlook non esegue l'istruzione .Value finché l'istruzione .Run (la macro di Excel) non è completa? –

2

Un modo molto semplice, è di implementare una serratura.

Questo codice è una soluzione rapida e sporca, verificando l'esistenza di un file, in un luogo predefinito.

in C:\Ask me question workflow.xlsm aggiungere questo sub:

Sub WrapAskMeFlow() 
    Dim tmpFile As String 
    tmpFile = "C:\AskMeFlow.tmp" 
    Open tmpFile for Output as #1 
    Close #1 
    AskMeFlow 
    Kill tmpFile 
End Sub 

in Outlook macro aggiuntivo:

Sub AskMeAlerts() 
Dim appExcel As Excel.Application 
Dim wkb As Excel.Workbook 
Set appExcel = CreateObject("Excel.Application") 
appExcel.Workbooks.Open ("C:\Ask me question workflow.xlsm") 
appExcel.Visible = True 
appExcel.Run "'Ask me question workflow.xlsm'!WrapAskMeFlow" 
appExcel.DisplayAlerts = False 
While Dir("C:\AskMeFlow.tmp")="":DoEvents:Wend 
While Dir("C:\AskMeFlow.tmp")<>"":DoEvents:Wend 
appExcel.ActiveWorkbook.Save 
appExcel.Quit Set appExcel = Nothing 
Set wkb = Nothing 
End Sub 
2

Opzione 1

L'opzione più semplice nel tuo caso specifico sarebbe quello di costruire il salva e chiudi i comandi nella macro di Excel anziché in quella di Outlook.

Che è, si potrebbe modificare il codice di Outlook per:

Sub AskMeAlerts() 
Dim appExcel As Excel.Application 
Dim wkb As Excel.Workbook 'Is this declaration necessary for some code elsewhere? You do not use this variable and I would recommend removing the declaration. 
     Set appExcel = CreateObject("Excel.Application") 
     With appExcel 
      .Workbooks.Open ("C:\Ask me question workflow.xlsm") 
      .Visible = True 
      .Run "'Ask me question workflow.xlsm'!AskMeFlow" 
     'No need to explicitly set alert values or save workbook as Excel macro will handle this. 
     End With 
     Set appExcel = Nothing 
     Set wkb = Nothing 'Again, is this necessary? 
End Sub 

Si potrebbe quindi aggiungere il seguente alla fine del "Chiedimi discussione workflow.xlsm" file:

Application.DisplayAlerts = False 
ThisWorkbook.Close SaveChanges:=True 
Application.Quit 

Nota: se esegui la macro manualmente o in altri casi d'uso in cui non vuoi che la cartella di lavoro salvi, chiudi e chiudi, potresti considerare l'aggiunta di una variabile di input alla macro AskMeFlow che è impostata su False ma è impostata su Vero da Outlook. Penso che questo sia leggermente al di fuori della portata di questa risposta, quindi non approfondirò ulteriormente, ma fammi sapere se sei interessato a questa opzione.

Opzione 2

Redacted. Vedi la soluzione di Uri; i miglioramenti che ho suggerito non modificano sostanzialmente questa soluzione.

Opzione 3

A seconda della natura del codice di Excel, si potrebbe trasformare in una funzione e catturare la variabile di uscita. Qualcosa come il qui sotto:

Sub AskMeAlerts() 
Dim appExcel As Excel.Application 
Dim wkb As Excel.Workbook 
Dim StrOutput as string 
StrOutput = "Excel macro did not complete." 
Set appExcel = CreateObject("Excel.Application") 
appExcel.Workbooks.Open ("C:\Ask me question workflow.xlsm") 
appExcel.Visible = True 
StrOutput = appExcel.Run "'Ask me question workflow.xlsm'!AskMeFlow" 
MsgBox StrOutput 
appExcel.DisplayAlerts = False 
appExcel.ActiveWorkbook.Save 
appExcel.Quit Set appExcel = Nothing 
Set wkb = Nothing 
End Sub 

Si potrebbe quindi cambiare AskMeFlow a una funzione e aggiungere il seguente codice:

Function AskMeFlow() as String 
AskMeFlow = "Uncaught error executing Excel code." 
'Your code here 
AskMeFlow = "Excel code completed successfully!" 
End Function 
+0

L'opzione 1 non risponde alle domande (ovvero rileva quando il codice Excel è completo). L'opzione 2 sembra, ma sta modificando una risposta esistente. – brettdj

+0

L'opzione 1 suggerisce, come l'opzione 1 precedente, che attendere la chiusura di Excel potrebbe non essere il miglior ricorso in questo specifico scenario. Il tuo suggerimento di portare il codice Excel in Outlook è funzionalmente equivalente al mio suggerimento di spostare tutte le azioni macro di Outlook che dipendono veramente dal completamento della macro di Excel nella macro di Excel. Non sto cercando di diventare un argomento polemico, e mi scuso se sta venendo fuori da quella parte; Grazie per il feedback. –

+0

Non ti sembra affatto polemico - hai sollevato una buona domanda. Allo stesso modo i miei commenti sui tuoi post sono stati concepiti come costruttivi, anche se altri standard di forum SO possono essere abbastanza bruschi e diretti. Capisco ora la logica che intendevi sull'opzione 1. – brettdj

1

Se Sub AskMeFlow fare calcoli senza l'intervento dell'utente, suppongo che si può semplicemente tenere traccia di Excel di CalculationState .

Sub AskMeAlerts() 
    With CreateObject("Excel.Application") 
     .Workbooks.Open ("C:\Ask me question workflow.xlsm") 
     .Visible = True 

     ' Ensure Autocalculation is on 
     .Calculation = -4105 ' xlCalculationAutomatic 

     .DisplayAlerts = False 
     .Run "'Ask me question workflow.xlsm'!AskMeFlow" 

     ' Wait until calculation is done 
     Do Until .CalculationState = 0 ' xlDone 
      DoEvents 
     Loop 

     .ActiveWorkbook.Save 
     .ActiveWorkbook.Close 
     .Quit 
    End With 
End Sub 

Sarebbe ancora meglio se il AskMeFlow è AUTO eseguito in Workbook_Open evento (all'interno di "ThisWorkbook" modulo).

1

La macro di Excel dovrebbe terminare chiudendo tutte le cartelle di lavoro, e Outlook aspetterebbe finché ci sono ancora cartelle di lavoro aperte.

In Excel:

// do work 
Application.ActiveWorkbook.Save 
Application.DisplayAlerts = False 
For Each wrkbk In Application.Workbooks 
    If wrkbk.Name <> ThisWorkbook.Name Then wrkbk.Close 
Next 
ThisWorkbook.Save 
ThisWorkbook.Close 

La macro di Outlook può piscina fino Workbooks.Count = a zero

While appExcel.Workbooks.Count > 0 :DoEvents:Wend 
appExcel.DisplayAlerts = False 
appExcel.Quit 
Set appExcel = Nothing 
-1

Terminare il codice con il seguente:

On Error Resume Next 
    On Error GoTo 0 

ExitFunction: 
    Set objShell = Nothing 

End Function