检测(在VBA中)何时包含excel实例的窗口变为活动状态

我可以看到WindowActivate事件发生,在各级,当我在Excel中切换窗口,但有没有办法引发事件时,Excel成为前台应用程序? 如果我点击Excel的工作,例如在浏览器中的一段时间,然后点击回到Excel窗口,我没有看到任何事件触发。 有什么办法可以检测到吗?

我想刷新我的VBA应用程序的一些元素,因为偶尔我发现基于超文本function的鼠标移动function失去了激活图表的能力。 我可以通过取消保护和保护工作表来解决这个问题,或者通过废弃和重新初始化我的对象的一个​​子集来解决这个问题。 我想在我正在寻找的事件上触发此操作。

我也可以通过SendKeys做到这一点,但它不是很好,因为它由于SendKeys中logging的错误而消除了键盘设置(例如滚动locking),并且使屏幕闪烁的次数超过了我的意愿。

由于代码将驻留在VBA中,因此我会将操作限制在特定的工作簿中。 如果在进入Excel实例窗口时不同的(被动)工作簿处于活动状态,则不会触发任何操作,并且如果用户select包含该工作簿的工作簿,则可以使用WorkbookActivate事件来刷新应用程序。

我相信这不是直接在Excel中提供的,所以使用Windows API。 你可以用VBA做win32编程!

说明

您可以使用win32 api函数SetWinEventHook让Windows向您报告某些事件。 包括在前景窗口更改时触发的EVENT_SYSTEM_FOREGROUND。 在下面的例子中,我根据Excel的进程ID检查新的前台窗口的进程ID。 这是一个简单的方法,但它会检测其他Excel窗口,如VBA窗口与主Excel窗口相同。 这可能是也可能不是你想要的行为,并可以相应地改变。

您必须小心使用SetWinEventHook,因为您将callback函数传递给它。 在这个callback函数中你可以做的事情是有限的,它存在于VBA的正常执行之外,它里面的任何错误都会导致Excel以不可恢复的混乱方式崩溃。

这就是为什么我使用Application.OnTime来报告事件。 如果多个事件比Excel和VBA更新更快地触发,它们不会保证顺序发生。 但它更安全。 您也可以更新一个集合或事件数组,然后单独在WinEventFunccallback之外单独读取这些事件。

示例代码

为了testing这个,创build一个新的模块并把这个代码粘贴进去。 然后运行StartHook。 请记住在closuresExcel或修改代码之前运行StopAllEventHooks! 在生产代码中,您可能会将StartEventHook和StopAllEventHooks添加到WorkBook_Open和WorkBook_BeforeClose事件,以确保它们在适当的时间运行。 请记住,如果挂钩停止之前WinEventFunc VBA代码发生了什么, Excel将会崩溃 。 这包括正在修改的代码或正在closures的工作簿。 在挂钩处于活动状态时,也不要按下VBA中的停止button。 停止button可以擦除当前的程序状态!

 Option Explicit Private Const EVENT_SYSTEM_FOREGROUND = &H3& Private Const WINEVENT_OUTOFCONTEXT = 0 Private Declare Function SetWinEventHook Lib "user32.dll" (ByVal eventMin As Long, ByVal eventMax As Long, _ ByVal hmodWinEventProc As Long, ByVal pfnWinEventProc As Long, ByVal idProcess As Long, _ ByVal idThread As Long, ByVal dwFlags As Long) As Long Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long Private pRunningHandles As Collection Public Function StartEventHook() As Long If pRunningHandles Is Nothing Then Set pRunningHandles = New Collection StartEventHook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 0&, AddressOf WinEventFunc, 0, 0, WINEVENT_OUTOFCONTEXT) pRunningHandles.Add StartEventHook End Function Public Sub StopEventHook(lHook As Long) Dim LRet As Long If lHook = 0 Then Exit Sub LRet = UnhookWinEvent(lHook) End Sub Public Sub StartHook() StartEventHook End Sub Public Sub StopAllEventHooks() Dim vHook As Variant, lHook As Long For Each vHook In pRunningHandles lHook = vHook StopEventHook lHook Next vHook End Sub Public Function WinEventFunc(ByVal HookHandle As Long, ByVal LEvent As Long, _ ByVal hWnd As Long, ByVal idObject As Long, ByVal idChild As Long, _ ByVal idEventThread As Long, ByVal dwmsEventTime As Long) As Long 'This function is a callback passed to the win32 api 'We CANNOT throw an error or break. Bad things will happen. On Error Resume Next Dim thePID As Long If LEvent = EVENT_SYSTEM_FOREGROUND Then GetWindowThreadProcessId hWnd, thePID If thePID = GetCurrentProcessId Then Application.OnTime Now, "Event_GotFocus" Else Application.OnTime Now, "Event_LostFocus" End If End If On Error GoTo 0 End Function Public Sub Event_GotFocus() Sheet1.[A1] = "Got Focus" End Sub Public Sub Event_LostFocus() Sheet1.[A1] = "Nope" End Sub 

这里有一个想法如何使用VBA实现: 检测Excel何时失去焦点?