Option Explicit
Enum Yes_No_Cancel
Yes = 6
No = 7
Cancel = 2
End Enum
Private Declare Function FindWindow Lib "user32.dll" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLong Lib "user32" _
Alias "GetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" _
(ByVal hwnd As Long, _
ByVal nIndex As Long, _
ByVal lNewLong As Long) As Long
Private Declare Function CallWindowProc Lib "user32" _
Alias "CallWindowProcA" _
(ByVal lpPrevWndFunc As Long, _
ByVal hwnd As Long _
, ByVal Msg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Private Declare Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" _
(ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
Private Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Long, _
ByVal nCode As Long, _
ByVal wParam As Long, _
lParam As Any) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Long) As Long
Private Declare Function GetCurrentThreadId Lib "kernel32" _
() As Long
Private Declare Function GetClassName Lib "user32.dll" _
Alias "GetClassNameA" _
(ByVal hwnd As Long, _
ByVal lpClassName As String, _
ByVal nMaxCount As Long) As Long
Private Declare Function SetTimer Lib "user32" _
(ByVal hwnd As Long, _
ByVal nIDEvent As Long, _
ByVal uElapse As Long, _
ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" ( _
ByVal hwnd As Long, _
ByVal uIDEvent As Long) As Long
Private Const WH_CBT = 5
Private Const GWL_HINSTANCE = (-6)
Private Const HCBT_CREATEWND = 3
Private Const GWL_WNDPROC = (-4)
Private Const WM_COMMAND = &H111
Private Const BN_CLICKED = 0
Private lCBTHook As Long
Private lPrevWnProc As Long
Public glLoword As Long
Private glHwnd As Long
Private glMsg As Long
Private glWparam As Long
Private glLparam As Long
'*******************
'Public Property.
'*******************
Public Property Let Monitor_Save_Changes(ByVal Status As Boolean)
'Set a CBT hook to catch the 'Save Changes' Wnd creation.
If Not ThisWorkbook.Saved And Status Then
lCBTHook = SetWindowsHookEx(WH_CBT, AddressOf CBTProc, _
GetAppInstance, GetCurrentThreadId)
End If
End Property
'*******************
'Private Routines.
'*******************
Private Function CBTProc _
(ByVal idHook As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long
Dim sBuffer As String
Dim lRetVal As Long
Select Case idHook
Case Is = HCBT_CREATEWND
'Some Wnd has been created within the excel process.
sBuffer = Space(256)
lRetVal = GetClassName(wParam, sBuffer, 256)
'Is it our 'SaveChanges' wnd ?
If Left(sBuffer, lRetVal) = "#32770" Then
'if so subclass it now.
lPrevWnProc = SetWindowLong _
(wParam, GWL_WNDPROC, AddressOf CallBack)
End If
'done with hook.
UnhookWindowsHookEx lCBTHook
End Select
'Call next hook if any.
CBTProc = CallNextHookEx _
(lCBTHook, idHook, ByVal wParam, ByVal lParam)
End Function
Private Function CallBack _
(ByVal hwnd As Long, ByVal Msg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
On Error Resume Next
Dim LowWord As Long, HighWord As Long
'Process the notifications sent by the buttons in the 'Save changes' Wnd.
Select Case Msg
Case WM_COMMAND
GetHiLoword wParam, LowWord, HighWord
Call SetWindowLong(hwnd, GWL_WNDPROC, lPrevWnProc)
'store the arg values for later use in the TimerProc
glHwnd = hwnd
glMsg = Msg
glWparam = wParam
glLparam = lParam
glLoword = LowWord
'run the timer
SetTimer Application.hwnd, 0, 1, AddressOf TimerProc
If LowWord = Yes_No_Cancel.Yes Or LowWord = Yes_No_Cancel.No Then
Exit Function
End If
End Select
CallBack = CallWindowProc _
(lPrevWnProc, hwnd, Msg, wParam, ByVal lParam)
End Function
Private Sub TimerProc()
On Error Resume Next
KillTimer Application.hwnd, 0
Call ThisWorkbook.MonitorProc(glLoword)
Call CallWindowProc _
(lPrevWnProc, glHwnd, glMsg, glWparam, ByVal glLparam)
End Sub
Private Sub GetHiLoword _
(wParam As Long, ByRef loword As Long, ByRef hiword As Long)
' this is the LOWORD of the wParam:
loword = wParam And &HFFFF&
' LOWORD now equals 65,535 or &HFFFF
' this is the HIWORD of the wParam:
hiword = wParam \ &H10000 And &HFFFF&
' HIWORD now equals 30,583 or &H7777
End Sub
Private Function GetAppInstance() As Long
GetAppInstance = GetWindowLong _
(FindWindow("XLMAIN", Application.Caption), GWL_HINSTANCE)
End Function