0. 背景和提示
在一个Excel和Access联动的大型Excel宏文件中,假设只能通过一个新的Excel宏文件去操控而不能改变原有的文件,且原文件会产生一些提示性的弹窗(例如“程序运行结束”)。如果要实现源文件在指定条件下循环开启和关闭,则需要解决弹窗的关闭或禁用,以下是一些可能得方法。
自动化关闭弹窗的方法都存在一定的不确定性,并且可能会在不同的环境和情况下产生不同的结果。在没有修改原始宏代码的情况下,任何自动处理弹窗的尝试都应该非常谨慎,并在一个受控的环境中多次进行充分的测试。
1. 弹窗(消息显示)
在VBA (Visual Basic for Applications) 中,用于显示弹窗的函数是MsgBox
。这个函数可以用来显示不同类型的对话框,并且可以返回用户点击的按钮的值。
MsgBox
函数的基本语法如下:
MsgBox(prompt[, buttons][, title][, helpfile, context])
prompt
是必需的,表示对话框中显示的文本信息。buttons
是可选的,用来指定对话框中显示的按钮类型和图标类型。title
是可选的,用来指定对话框的标题。helpfile
和context
是可选的,用来指定帮助文件和帮助文件中上下文的ID号。
buttons
参数可以是以下值的组合:
-
按钮类型:
vbOKOnly
:只显示“OK”按钮。vbOKCancel
:显示“OK”和“Cancel”按钮。vbAbortRetryIgnore
:显示“Abort”、“Retry”和“Ignore”按钮。vbYesNoCancel
:显示“Yes”、“No”和“Cancel”按钮。vbYesNo
:显示“Yes”和“No”按钮。vbRetryCancel
:显示“Retry”和“Cancel”按钮。vbCancelTryAgainContinue
:显示“Cancel”、“Try Again”、“Continue”按钮(VBA 6.0 之后版本)。
-
图标类型:
vbCritical
:显示严重错误图标。vbQuestion
:显示警告查询图标。vbExclamation
:显示警告消息图标。vbInformation
:显示信息图标。
-
默认按钮:
vbDefaultButton1
:第一个按钮为默认按钮。vbDefaultButton2
:第二个按钮为默认按钮。vbDefaultButton3
:第三个按钮为默认按钮。vbDefaultButton4
:第四个按钮为默认按钮(如果有的话)。
-
模态性:
vbApplicationModal
:应用程序模态,用户必须先响应该消息框,然后才能继续在当前应用程序中工作。vbSystemModal
:系统模态,所有应用程序都被挂起,直到用户响应了消息框。
MsgBox
函数返回值是一个整数,表示用户点击的按钮,如下:
vbOK
:点击了“OK”。vbCancel
:点击了“Cancel”。vbAbort
:点击了“Abort”。vbRetry
:点击了“Retry”。vbIgnore
:点击了“Ignore”。vbYes
:点击了“Yes”。vbNo
:点击了“No”。vbTryAgain
:点击了“Try Again”(VBA 6.0 之后版本)。vbContinue
:点击了“Continue”(VBA 6.0 之后版本)。
这是MsgBox
函数的一个基本示例:
Sub ShowMessageBox()
Dim response As Integer
' 显示一个简单的对话框,有一个“OK”按钮和一个标题
response = MsgBox("这是一个简单的消息框", vbOKOnly, "示例标题")
If response = vbOK Then
' 用户点击了"OK"
MsgBox "用户点击了OK"
End If
End Sub
2. 方法一:预防性策略
在运行这些宏之前,如果可能,最好的解决方案是沟通并协调修改宏的代码,以便于它们在特定条件下不显示弹窗。在 Excel VBA 中,可以通过多种方式实现控制宏运行时是否显示弹窗的机制。一种常见且简单的方法是使用一个全局变量作为标志来控制弹窗的显示。通过这种方式,可以灵活地控制宏在运行时是否弹出消息框,而无需删除或注释掉原有的 MsgBox
调用,从而使得宏的行为可以根据上下文的需要动态调整。以下是在宏内部实现这种检查机制的步骤:
2.1 定义全局变量
在 VBA 编辑器的一个模块中定义一个全局变量。这个变量将用来指示是否显示弹窗。
' 在模块的顶部定义全局变量
Global SuppressMessages As Boolean
2.2 修改宏以检查全局变量
在可能弹出消息框的宏代码中,添加条件检查,根据全局变量 SuppressMessages
的值决定是否显示弹窗。
Sub MacroThatShowsMessage()
' 检查是否应该显示弹窗
If Not SuppressMessages Then
MsgBox "这是一个重要的消息。"
End If
' 其他宏代码...
End Sub
2.3 控制全局变量
在调用宏之前,根据需要设置 SuppressMessages
变量的值。如果想要隐藏弹窗,就在调用宏之前设置它为 True
。
Sub RunMacroWithoutMessages()
' 设置变量以抑制消息
SuppressMessages = True
' 调用可能会显示消息的宏
Call MacroThatShowsMessage
' 重置变量,以便后续的宏调用可以正常显示消息
SuppressMessages = False
End Sub
2.4 确保全局变量的状态
最好的实践是在宏结束时重置全局变量,以确保它不会影响到其他宏。
Sub MacroThatShowsMessage()
' 保存原始的SuppressMessages值
Dim originalSuppressMessages As Boolean
originalSuppressMessages = SuppressMessages
' 检查是否应该显示弹窗
If Not SuppressMessages Then
MsgBox "这是一个重要的消息。"
End If
' 其他宏代码...
' 重置SuppressMessages到宏开始前的值
SuppressMessages = originalSuppressMessages
End Sub
2.5 额外tips
- 如果宏被存储在一个添加了密码的工作簿中,可能需要在调用宏之前先解锁VBA项目。
- 如果宏是由其他应用程序(如通过 COM 对象的 Excel 实例)触发的,确保设置全局变量的代码在正确的上下文中执行。
- 如果有多个宏可能会弹出消息框,则需要确保所有这些宏都检查了
SuppressMessages
变量。
3. 方法二:Windows API
通过调用 Windows API (Application Programming Interface) 函数来增强宏的功能和模拟用户交互。
API 函数执行许多在 VBA 中不直接可用的操作,例如访问低级系统信息、创建复杂的窗口消息框或修改窗口的行为。但这种方法应谨慎使用,因为它可能会影响用户的其他操作,并且可能会受到 Excel 安全设置的限制。以下是如何使用 Windows API 来控制宏运行时是否显示弹窗的示例:
3.1 声明 API 函数
首先,需要在 VBA 中声明将要使用的 API 函数。对于弹窗控制,MessageBox
API 函数是一个可用的选择。需要在模块的顶部用 Declare
语句声明这个函数。
' 声明API函数
#If VBA7 Then
Declare PtrSafe Function MessageBox Lib "user32" Alias "MessageBoxA" ( _
ByVal hwnd As LongPtr, _
ByVal lpText As String, _
ByVal lpCaption As String, _
ByVal uType As Long) As Long
#Else
Declare Function MessageBox Lib "user32" Alias "MessageBoxA" ( _
ByVal hwnd As Long, _
ByVal lpText As String, _
ByVal lpCaption As String, _
ByVal uType As Long) As Long
#End If
这里使用了条件编译来区分 32 位和 64 位的 VBA 环境。PtrSafe
关键字在 64-bit 环境中是必须的,以确保指针的正确使用。
3.2 使用 API 函数
接着,使用声明的 MessageBox
函数来显示自定义的消息框。可以根据需要控制消息框的内容和样式。
Sub ApiMessageBoxDemo()
' 使用 MessageBox API 函数来显示弹窗
Dim response As Long
response = MessageBox(0, "这是通过 Windows API 显示的消息。", "API 消息框", 0)
' 根据用户响应执行操作
If response = 1 Then ' 用户点击了“确定”按钮
' 执行相关操作
End If
End Sub
3.3 条件控制显示
为了控制是否显示弹窗,可以引入一个全局变量作为开关。
Global ShowApiMessageBox As Boolean
Sub ControlApiMessageBox()
ShowApiMessageBox = False ' 设置为 True 或 False 来控制是否显示弹窗
If ShowApiMessageBox Then
ApiMessageBoxDemo
End If
End Sub
在这个例子中,ShowApiMessageBox
将控制是否调用 ApiMessageBoxDemo
过程来显示消息。
3. 4 警告
- 当调用 Windows API 时,必须理解该函数的所有参数,因为错误的使用可能会导致不稳定的行为甚至崩溃。
- 由于 Windows API 函数是在操作系统层面上运行的,因此错误的参数或调用可能会导致更加严重的后果,比在 VBA 环境中出错要严重。
- 当分发包含 API 调用的工作簿时,请确保目标计算机的操作系统与 API 函数兼容。
- 此策略可能违反某些公司的IT政策。
4. 方法三:SendKeys 方法
SendKeys
是 VBA 提供的一个方法,它模拟键盘输入,向 Windows 环境发送一个或多个按键。这个方法的一个常见用途是自动化一些不能直接通过 VBA 控制的用户界面操作,比如在没有直接方法可用的情况下点击按钮。下面是如何在 VBA 中使用 SendKeys
方法:
4.1 SendKeys 基本语法
SendKeys
方法接受一个字符串参数,代表要发送的按键,以及一个可选的布尔值,用于指示操作是否为等待模式。字符串中的特定字符可以代表不同的按键或按键组合。例如:
{ENTER}
代表回车键。+{F1}
代表 Shift+F1。%{F4}
代表 Alt+F4。^{ESC}
代表 Ctrl+Esc。
4.2 在宏中使用 SendKeys
在宏中使用 SendKeys
来模拟按键。以下是两个简单的例子:
Sub CloseActiveWindow()
' 用 Alt+F4 组合键来关闭当前活动窗口
SendKeys "%{F4}", True
End Sub
Sub CopyPasteUsingSendKeys()
' 选定要复制的范围
Range("A1:A10").Select
' 模拟 Ctrl+C 复制
SendKeys "^c"
' 等待一小段时间确保复制完成
Application.Wait Now + TimeValue("00:00:01")
' 选择粘贴的目标单元格
Range("B1").Select
' 模拟 Ctrl+V 粘贴
SendKeys "^v"
End Sub
在这段代码中,^c
和 ^v
分别模拟了 Ctrl+C 和 Ctrl+V 的按键操作,用于复制和粘贴数据。
如果弹窗只是一个简单的确认对话框(OK按钮),发送 Enter 键的按键即可关闭它。
Sub ClickOKButton()
Application.DisplayAlerts = False
' ... 其余代码 ...
' 在运行宏之前发送 Enter 键的按键
SendKeys "~", True
' 调用其他工作簿中的宏
Application.Run "'******.xlsm'!import"
Application.Run "'******.xlsm'!output"
' ... 其余代码 ...
Application.DisplayAlerts = True
End Sub
在上述代码中,SendKeys "~", True
表示发送 Enter 键(“~” 是 Enter 键的符号),True
参数表示等待按键被处理。
这种方法存在缺点,最大的问题是它依赖于弹窗的出现时机必须与 SendKeys
调用精确同步。如果弹窗延迟出现,或者 SendKeys
在弹窗出现之前执行,那么按键将不会关闭弹窗。此外,SendKeys
可能会影响用户的其他操作,因为它是在全局范围内模拟按键。
4.3 警告
SendKeys
是一个不稳定的方法,因为它依赖于当前的屏幕焦点。如果焦点突然改变(比如用户点击了鼠标或按了键盘),SendKeys
发送的按键可能会被发送到错误的应用程序。SendKeys
方法在执行时不会等待前一个命令完成。如果需要在发送键之间有时间间隔,可以使用Application.Wait
或Sleep
函数(后者需要声明 Windows API)。- 使用
SendKeys
时,确保关闭所有可能会拦截按键的屏幕保护程序或其他安全软件。 - 如果代码需要在不同机器上运行,那么
SendKeys
方法可能因为系统设置的差异而不可靠。
5. 其他方法
Sub ClickOKButton()
' 禁用弹窗
Application.DisplayAlerts = False
' 调用其他工作簿中的宏
Application.Run "'******.xlsm'!import"
Application.Run "'******.xlsm'!output"
' 启用弹窗
Application.DisplayAlerts = True
End Sub
在VBA中,Application.DisplayAlerts
属性用于控制大多数情况下由Excel自动生成的警告和提示消息框的显示。将 Application.DisplayAlerts
设置为 False
会关闭这些警告框,例如在删除工作表或关闭工作簿时弹出的确认框。
然而,Application.DisplayAlerts
并不能关闭所有的弹窗。特别是,它不会关闭通过VBA代码显示的消息框(MsgBox
函数创建的),或者由其他程序创建的对话框。如果 import
和 output
宏中有使用 MsgBox
显示的消息框,那么将 Application.DisplayAlerts
设置为 False
并不会阻止这些消息框的显示。