Excel中的MsgBox焦点

7
我正在Excel中使用VBA计算大量数据,希望在完成后显示一个MsgBox。实际上,MsgBox会显示计算所花费的时间。
问题在于当用户在进行计算时决定做其他事情时。Excel继续计算,当计算完成时,MsgBox确实会显示,但由于某种原因,Excel不会将焦点给予MsgBox。Excel图标会在任务栏中闪烁,如果我们点击它,Excel会最大化,但是MsgBox却在Excel窗口后面,我们永远无法点击它。因此,唯一的方法是通过taskkill excel.exe来退出...这并不是很好。Alt+Pause也不起作用,因为代码只有在当前代码行结束(当MsgBox关闭时)后才会停止。
之前我尝试过在函数AppActivate(“Microsoft Excel”)之前操作,但没有任何成功(如何将焦点放在msgbox上?)。应用程序名称实际上比那更长,因为Excel 2010会将文档名称添加到窗口标题中。
有什么办法可以解决这个令人讨厌的问题吗?

1
这不是一个解决方案,但是按下Alt-Tab可以到达那里吗? - peege
1
不,我也试过了。在Windows 7中,MsgBox不被视为窗口,所以我只能看到Excel 2010并将焦点放在Excel 2010上。但在Excel中,焦点仍然在MsgBox的背后。不过我可以尝试用键盘按下“Enter”,这是我没试过的唯一方法。 - dan
1
你使用多个显示器吗?另外,你能发布一些代码吗? - peege
1
我确实有两个显示器。通常情况下,Excel甚至都没有被最小化。但是当我在另一个屏幕上做一些事情时(似乎一旦Excel失去焦点),MsgBox就会出现在它的下面。 - dan
2
另外,消息框必要吗?你可以使用其他通知方式,如用户表单、电子邮件通知、状态栏等。 - David Zemens
显示剩余7条评论
5个回答

6
无论其他应用程序有哪些焦点,这在Excel中都可以实现:
在消息框或任何警告之前放入以下代码:
AppActivate Application.Caption
DoEvents

相信我,这真是太棒了!


1
我需要试一下。我将在显示 MsgBox 之前放置它?问题是,1年前(!),MsgBox 会在用户没有注意到的情况下显示在后台。由于 Excel/Access 中的 MsgBox 不会在 Windows 中创建任务,因此无法将焦点设置为 MsgBox,唯一的解决方法是杀死应用程序或在聚焦应用程序后按 Enter(尽管 MsgBox 仍然在后面,但它通过主应用程序(Excel)获得了焦点)。 - dan
1
如果Excel在最近激活的应用程序所在的监视器上,则此方法无效。我会进一步研究它... - Doug Coats
@DougCoats,你能再解释一下吗?这是我唯一成功让msgbox获得焦点的方法。 - DeerSpotter
1
更详细地解释一下:我在显示器上打开了 Excel,而在弹出消息框之前打开的互联网页面也在同一台显示器上。当发生这种情况时,消息框将无法设置焦点。如果我将 Excel 放在另一个显示器上,它就可以正常工作。 - Doug Coats
1
即使我在同一显示器中打开一个覆盖第三方应用程序/窗口,也可以对其进行操作并弹出消息框。 - 6diegodiego9
显示剩余3条评论

3
似乎宏过程导致应用程序无响应。不确定是否有帮助,但您是否考虑在长时间运行的过程中添加DoEvents或Sleep(API调用)以将控制权交还给操作系统? Sleep是一个API调用,因此您需要在模块中声明才能使用它。 DoEvents可以防止应用程序锁定,但会使用更多的CPU,因此如果它在循环中,我建议每隔一段时间(30%或更少的迭代)对其进行访问。如果不是循环,并且您知道长时间运行过程中的瓶颈在哪里,则可以在每个长时间运行过程后调用DoEvents。
#If VBA7 And Win64 Then
' 64 bit Excel
Public Declare PtrSafe Sub Sleep Lib "kernel32" ( _
    ByVal dwMilliseconds As LongLong)
#Else
' 32 bit Excel
Public Declare Sub Sleep Lib "kernel32" ( _
    ByVal dwMilliseconds As Long)
#End If

Sleep API源码

然后在您的进程中

Sub SomeLongProcessWithDoEventsExample()
   For i = 1 to 100000
       'Some lengthy code
       If i Mod 333 = 0 Then
          DoEvents
       End If
   Next i
End Sub

Sub SomeLongProcessWithSleepExample()
   For i = 1 to 100000
       'Some lengthy code
       If i Mod 333 = 0 Then
          Sleep 1 * 1000 'Millseconds
       End If
   Next i      
End Sub

我建议将Application.ScreenUpdating = False设置为假,然后在进程完成后再打开它,但这可能会使问题变得更糟。
更新:
刚刚读到输入我的答案时输入的评论。除了消息框之外的另一个选择是,在所有文件被创建后打开保存文件的文件夹窗口(将Environ $(“APPDATA”)替换为保存位置)。
Shell "explorer.exe" & " " & Environ$("APPDATA"), vbMaximizedFocus

或者打开其中一个PDF文件:

Shell Environ$("COMSPEC") & " /c Start C:\SomeFile.pdf", vbMaximizedFocus

另一种选择

我无法在评论中放置此内容,因为有太多的代码,但可以调用MessageBox API,但不要设置消息框(hWnd)的所有者,请将其设置为&H0或&O0。vbSystemModal应该使其弹出到顶部。我不知道是否允许用户单击确定后选择Excel应用程序窗口:

MessageBox &O0, "My Message", "My Caption", vbOKOnly + vbSystemModal


#If VBA7 And Win64 Then
Public Declare PtrSafe Function MessageBox _
    Lib "User32" Alias "MessageBoxA" _
       (ByVal hWnd As LongLong, _
        ByVal lpText As String, _
        ByVal lpCaption As String, _
        ByVal wType As LongLong) _
    As Long

#Else
Public Declare Function MessageBox _
    Lib "User32" Alias "MessageBoxA" _
       (ByVal hWnd As Long, _
        ByVal lpText As String, _
        ByVal lpCaption As String, _
        ByVal wType As Long) _
    As Long

#End If

1
如果其他方法都不起作用,打开文件夹绝对是解决问题的好方法。谢谢,我会在周一研究您提供的第一个解决方案和其他解决方案。 - dan
1
所有的解决方案都不起作用,所以我现在试图规避它,并且不使用 MsgBox。打开文件夹确实解决了问题,并且它可以工作。感谢你的帮助。唯一的问题是,即使使用了 vbMaximizedFocus,焦点也不会进入文件夹,所以我必须查看任务栏才能知道它是否真正存在。我会尝试使用窗体来看看是否有改善。 - dan
1
用户窗体确实会将焦点放在Excel上(任务栏中的橙色闪烁),并且工作得很好,尽管Excel图表在后台闪烁。我将尝试使用ScreenUpdating选项来查看它是否有所不同,但这可能是我要坚持的解决方法。 - dan
1
听起来你已经接近成功了。通常我会在错误处理标签处重新开启屏幕更新(所有代码都放在错误处理标签中,然后检查错误代码是否为<> 0等),或者如果在错误处理标签之前退出,则可以将其放在错误处理程序中。 - Charles Byrne
是的,在显示表单之前将ScreenUpdating设置为false后,它不再闪烁。我需要在生成PDF时保持其开启,因为我正在使用Worksheet_Change()事件来编辑我的图表标签位置,以避免它们重叠(即使使用xlLabelPositionBestFit也会发生),所以这使得它有点复杂。无论如何,它与表单一起工作。这仍然没有解决MsgBox进入后台的原始问题,但我认为这与我们业务计算机上的Excel或Windows中的一个非常特定的设置有关。 - dan
显示剩余2条评论

3

*哎呀,我没有读完你的问题,我使用了AppActivate并且按预期工作,我正在使用MS Office Professional Plus 2010

我无法让guitarthrower方法工作,但是能够通过此示例使其工作。请尝试使用AppActivate ("Microsoft excel")而不是 ThisWorkbook.Activate,然后再使用MsgBox

Sub test()
    If Application.Wait(Now + TimeValue("0:00:10")) Then
        AppActivate ("Microsoft excel")
        MsgBox "Time expired"
    End If
End Sub

1
无论我使用AppActive还是ThisWorkbook.Activate,都不起作用。Application.Wait似乎也没有帮助。请注意,当我单击Excel时,我可以看到工作簿中的元素不断闪烁,等待对MsgBox(在后台隐藏)的响应。按Enter键似乎并不总是有效,但通常需要组合使用Ctrl+Break、Esc和Enter键。 - dan
1
很奇怪,我运行这个程序时可以将Excel最小化,然后只有消息框弹出。不知道为什么 :( - mrbungle
1
这可能与我们商业电脑上配置的Excel和Windows有关。虽然它并没有完全解决问题,但我会采用基于表单的解决方法。 - dan
1
@dnLL:应该是AppActivate,而不是AppActive。 - 6diegodiego9

2

我进行了一些测试,并为您找到了一个潜在的解决方法。

我设置了这个简单的程序来测试您的情况:

Sub test()
    If Application.Wait(Now + TimeValue("0:00:10")) Then
        MsgBox "Time expired"
    End If
End Sub

我运行了这个程序,并将所有窗口最小化,但当定时器结束后,没有任何反应。如果我切换到Excel界面,我就可以看到消息框,但在其他情况下则不行。

所以我尝试了这个方法:

Sub test()
    If Application.Wait(Now + TimeValue("0:00:10")) Then
        ThisWorkbook.Activate
        MsgBox "Time expired"
    End If
End Sub

当我运行这个过程并最小化所有窗口时,与其什么也没看到相反,消息框弹出了(但Excel窗口没有弹出)。
我认为在你的MsgBox代码之前添加ThisWorkbook.Activate可以让你的文件中发生同样的事情。
这并不能完全解决问题,但希望比你现在的情况要好。

1
我尝试了ThisWorkbook.Activate但是它没有帮助,很遗憾。我不确定到底有什么问题,但我尝试了另一台电脑,出现了同样的问题。但我们公司所有的电脑在硬件和软件方面几乎都是相同的。我可能会选择完全不同的解决方案,比如打开文件夹而不是使用MsgBox。 - dan

2

我尝试了其他所有答案:

  • ThisWorkbook.Activate
  • AppActivate()
  • Application.Wait()
  • Sleep

但出于某种原因,以上方法都不起作用。我相信我们电脑环境中存在某些非常特定的商业设置可能会导致问题。正如其他人所提到的,在任何全新安装了 Windows 7 和 Office 2010 的计算机上,即使是双显示器设置,以上所有解决方案应该都能起作用。所以这里给所有这些答案点一个赞。同时,我注意到这个问题只发生在某些特定的工作簿中,这真的很奇怪,可能只是由于我在工作簿中执行的某些操作(无论是 VBA 还是 Excel)与 Office 2010 相关的 bug。

话虽如此,我的真正解决办法(其实并不是解决最初的问题)是不使用 MsgBox()。有几个变通方法可以使用,我发现表单是最好的。所以,为了不再浪费时间在这个问题上,我得出了以下非常简单的代码来替代原来的 MsgBox()

Application.ScreenUpdating = False
frmMsgBox.Show
Application.ScreenUpdating = True

我可以在 frmMsgBox 标签中放任何我想要的文本,由于这是 Excel,我可以通过使用隐藏单元格来传递参数。
感谢所有的帮助。

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