VBA Outlook调用Excel宏并等待宏完成

14

我正在从Outlook规则脚本中调用Excel宏。

该过程是: 获取邮件,运行一个Outlook规则,该规则运行一个Outlook脚本,从该脚本打开Excel,运行Excel宏,关闭Excel。

如何在Outlook规则脚本中验证Excel宏是否完成,以保存并关闭应用程序?

 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

请展示您代码中相关的部分(即不是全部代码)。 - Jean-François Corbett
Dim appExcel As Excel.Application Dim wkb As Excel.WorkbookSet 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 = NothingEnd Sub - user3016795
1
我想你自己也能看出这完全是无法辨认的...请编辑你的问题,将代码放在那里,并使用“{}”按钮进行适当的格式化。 - Jean-François Corbett
感谢您的编辑。我没有看到我实际上可以编辑主要问题。在主要问题中,我知道如何呈现代码... - user3016795
OP上次出现是在2月21日10:50。只是错过了这些珍珠。其他人可能会受益。https://blog.stackoverflow.com/2011/06/optimizing-for-pearls-not-sand/ - user5942421
6个回答

4

您可以选择以下两种方式:

  1. 将您的Excel宏移植到Outlook并直接运行
  2. 使用一个标志来捕获代码完成情况

下面的代码使用第一个sheet的A1单元格中的标记来捕获正在运行的代码(在Excel部分)。我还整理了您的代码(它是早期和后期绑定的混合)。

Outlook 代码

 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

Excel代码

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

我可能会错误地理解你的代码,但是 AskMeAlerts() 子程序难道不会简单地将 .activeworkbook.sheets(1).[a1].Value = "Complete" 评估为 False,然后跳过 If 块吗?你是否应该添加某种等待功能,例如 Do 或 While 循环? - Nick Peranzi
@NickPeranzi 不是的。请尝试按照上述方式测试代码(在 .Run 行之前和之后添加 MsgBox .activeworkbook.sheets(1).Range("A1").Value)。 - brettdj
那么,“flag”是否重要呢?由于在Outlook宏执行.Run语句和检查值之间没有等待,这是否意味着Outlook宏直到完成Excel宏的.Run语句后才执行.Value语句? - Nick Peranzi
这是关于序列的正确的理解。但是,原帖作者可能有合理的担忧,即Excel宏无法按照期望的方式完成(因为我们没有看到Excel代码)。因此,这更多是一种验证,而不是像您所说的要求。 - brettdj

2

选项1

在您的情况下,最简单的选择是将保存和退出命令构建到Excel宏中,而不是Outlook宏。

也就是说,您可以修改Outlook代码:

 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

您可以将以下内容添加到“Ask me question workflow.xlsm”文件的末尾:
 Application.DisplayAlerts = False
 ThisWorkbook.Close SaveChanges:=True
 Application.Quit

注意:如果您还将手动运行宏或在其他情况下不希望工作簿保存、关闭和退出,您可以考虑向AskMeFlow宏添加一个输入变量,默认值为False,但由Outlook设置为True。我认为这略微超出了本答案的范围,所以我不会进一步阐述,但如果您对此选项感兴趣,请告诉我。
选项2已被删除。请参见Uri的解决方案;我建议的改进并没有从根本上改变那个解决方案。
选项3:根据Excel代码的性质,您可以将其转换为函数并捕获输出变量。类似于以下内容:
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

您需要将AskMeFlow更改为函数,并添加以下代码:

然后您需要将AskMeFlow更改为函数,并添加以下代码:

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

选项1没有解决问题(即检测Excel代码何时完成)。选项2似乎可以,但它只是调整现有的答案。 - brettdj
选项1建议,与您上面的选项1一样,在这种特定情况下等待Excel关闭可能不是最好的选择。您将Excel代码移植到Outlook的建议在功能上等同于我的建议,即将所有真正依赖于Excel宏完成的Outlook宏操作移动到Excel宏中。我并不想显得争论,如果有这种感觉,我很抱歉;感谢您的反馈。 - Nick Peranzi
你的提问并没有显得争论,反而是一个很好的问题。同样地,我对你的帖子发表的评论也是建设性的,尽管在其他论坛标准下,SO可能会显得有些突兀和直接。我现在理解了你在选项1上的逻辑。 - brettdj
@brettdj - 你觉得我在编辑后的回答中提出的函数建议怎么样?感谢你抽出时间来审阅。 - Nick Peranzi

2

一个非常简单的方法是实现锁。

这段代码是一种快速而简单的解决方案,通过检查预定义位置上文件的存在来实现。

C:\Ask me question workflow.xlsm 中添加以下子程序:

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

在您的Outlook宏中添加以下内容:
 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

1
Excel宏应该在关闭所有工作簿后结束,而Outlook将等待仍有工作簿打开。
在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

Outlook宏可以等待,直到Workbooks.Count = zero

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

1
如果Sub AskMeFlow在没有用户干预的情况下进行计算,我想你可以简单地跟踪Excel的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

如果AskMeFlow在" ThisWorkbook "模块的Workbook_Open事件中自动执行,那就更好了。


-1
在你的代码结尾处添加以下内容:
    On Error Resume Next
    On Error GoTo 0

ExitFunction:
    Set objShell = Nothing

End Function

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接