问题
每当主Excel窗口被调整大小时,调用一个过程。
第一次尝试:
Sub Workbook_WindowResize(ByVal Wn As Window)
Debug.Print Wn.Width & "x" & Wn.Height
End Sub
结果:
每当“内部”工作簿窗口大小调整时,子例程都会被调用,但当应用程序窗口大小调整时不会调用。即在包含应用程序实例的多文档界面子项上进行调整。
第二次尝试
Dim WithEvents App As Application
Private Sub App_WindowResize(ByVal Wb As Workbook, ByVal Wn As Window)
Debug.Print Wn.Width & "x" & Wn.Height
End Sub
结果:
奇怪的是,之前发生的同样的事情又发生了,这让我感到惊讶。事件只在工作簿窗口调整大小时发生,而不是应用程序窗口。
因此,我开始研究使用Windows API。
有许多使用Windows API设置系统范围键盘和鼠标钩子的示例。这是相同的原理:
Public Enum enHookTypes
WH_CALLWNDPROC = 4
WH_CALLWNDPROCRET = 12
WH_CBT = 5
WH_DEBUG = 9
WH_FOREGROUNDIDLE = 11
WH_GETMESSAGE = 3
WH_HARDWARE = 8
WH_JOURNALPLAYBACK = 1
WH_JOURNALRECORD = 0
WH_MOUSE = 7
WH_MSGFILTER = (-1)
WH_SHELL = 10
WH_SYSMSGFILTER = 6
WH_KEYBOARD_LL = 13
WH_MOUSE_LL = 14
WH_KEYBOARD = 2
End Enum
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As LongPtr, ByVal lpfn As Long, ByVal hMod As Long, ByVal dwThreadId As Long) As LongPtr
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Private Declare Function GetLastError Lib "kernel32" () As Long
'Ensure that your hook procedure does not interfere with the normal operation of other hook procedures
Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal ncode As Long, ByVal wParam As Long, lParam As Any) As Long
Public hndl As Long
Sub HookWindow()
hndl = SetWindowsHookEx(WH_CALLWNDPROC, AddressOf measureWindow, Application.Hinstance, 0&)
Debug.Print hndl & "~~" & GetLastError()
End Sub
Sub unhookWindow()
ret = UnhookWindowsHookEx(hndl)
Debug.Print ret
End Sub
Public Sub measureWindow(code As Long, wParam As Long, lParam As Long)
If code > 0 Then
Debug.Print ThisWorkbook.Windows(1).Width & "x" & ThisWorkbook.Windows(1).Height
Else
ret = CallNextHookEx(measureWindow, code, wParam, lParam)
End If
End Sub
结果:
如果我替换掉以下内容中的 WH_CALLWNDPROC
:
hndl = SetWindowsHookEx(WH_CALLWNDPROC, AddressOf measureWindow, Application.Hinstance, 0&)
使用 WH_KEYBOARD_LL
后,每次按键时都会调用子程序。同样地,如果我将其替换为 WH_MOUSE_LL
,则每当鼠标移动或按下鼠标按钮时都会调用子程序。
问题在于,当我尝试将子程序钩入 WH_CALLWNDPROC
时,什么也没有发生?
为什么?
我仍然不确定,但对于 enHookTypes
中的所有 ENUM(除了 WH_MOUSE_LL
和 WH_KEYBOARD_LL
),情况也是如此。浏览 WinAPI 文档时,我读到您可以使用来自 kernel32.dll 的 GetLastError
来获得一些有关操作失败原因的指示。
到目前为止,我获得的错误号码(以十进制表示)为 错误5
(用于 JOURNAL 钩子),其余的为 错误1428
。
最终这也失败了。
1428
的翻译是 "不能在没有模块句柄的情况下设置非本地钩子。" 这是可以预料的。您正在安装全局钩子,但指定了任意模块的模块句柄(在本例中为应用程序的主模块)。这应该是实现钩子过程的模块。要么将您的钩子过程放入 DLL 中,并传递该模块句柄,要么安装本地(线程)钩子。不清楚您为什么一开始就尝试安装全局钩子。 - IInspectableSUBCLASSPROC
并非易事(委婉地说)。 - CominternEM_REQUESTRESIZE
消息。普通窗口在调整大小时会接收到WM_SIZE
、WM_SIZING
、WM_WINDOWPOSCHANGED
等一些其他消息。它们在不同的时间和不同的目的下使用。你需要查找它们以确定需要处理哪一个。如果不确定,请使用类似 Spy++ 的工具观察窗口消息在调整窗口大小时生成的情况。 - IInspectable