|
Option Explicit |
|
|
|
Private Declare Function SetWindowLong Lib "user32.dll" _ |
|
Alias "SetWindowLongA" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal nIndex As Long, _ |
|
ByVal dwNewLong 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 FindWindow Lib "user32.dll" _ |
|
Alias "FindWindowA" _ |
|
(ByVal lpClassName As String, _ |
|
ByVal lpWindowName As String) As Long |
|
|
|
Private Declare Function GetDesktopWindow Lib _ |
|
"user32.dll" () As Long |
|
Private Declare Function ShowWindow Lib "user32.dll" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal nCmdShow As Long) As Long |
|
|
|
Private Declare Function SendMessage Lib "user32.dll" _ |
|
Alias "SendMessageA" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal wMsg As Long, _ |
|
ByVal wParam As Long, _ |
|
ByRef lParam As Any) As Long |
|
|
|
Private Declare Function PostMessage Lib "user32.dll" _ |
|
Alias "PostMessageA" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal wMsg As Long, _ |
|
ByVal wParam As Long, ByVal lParam As Long) As Long |
|
|
|
Private Declare Function SetTimer Lib "user32.dll" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal nIDEvent As Long, _ |
|
ByVal uElapse As Long, _ |
|
ByVal lpTimerFunc As Long) As Long |
|
|
|
Private Declare Function KillTimer Lib "user32.dll" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal nIDEvent As Long) As Long |
|
|
|
Private Declare Function LockWindowUpdate Lib "user32.dll" _ |
|
(ByVal hwndLock As Long) As Long |
|
|
|
Private Declare Function GetProp Lib "user32" _ |
|
Alias "GetPropA" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal lpString As String) As Long |
|
|
|
Private Declare Function SetProp Lib "user32" _ |
|
Alias "SetPropA" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal lpString As String, _ |
|
ByVal hData As Long) As Long |
|
|
|
Private Declare Function RemoveProp Lib "user32" _ |
|
Alias "RemovePropA" _ |
|
(ByVal hwnd As Long, _ |
|
ByVal lpString As String) As Long |
|
|
|
Private Const GWL_WNDPROC As Long = -4 |
|
Private Const WM_USER As Long = &H400 |
|
Private Const WM_NCMOUSEMOVE As Long = &HA0 |
|
Private Const WM_SETREDRAW As Long = &HB |
|
|
|
Private Const VBE_CLASS_NAME As String _ |
|
= "wndclass_desked_gsk" |
|
Private Const EXCEL_CLASS_NAME As String _ |
|
= "XLMAIN" |
|
|
|
Private lOldWinProc As Long |
|
Private lVBEhwnd As Long |
|
|
|
Sub Safe_Subclass(hwnd As Long) |
|
|
|
'don't subclass the window twice ! |
|
If GetProp(GetDesktopWindow, "HWND") <> 0 Then |
|
MsgBox "The Window is already Subclassed.", _ |
|
vbInformation |
|
Exit Sub |
|
End If |
|
|
|
'store the target window hwnd as a desktop |
|
'window for later use property. |
|
|
|
SetProp GetDesktopWindow, "HWND", hwnd |
|
|
|
'retrieve the VBE hwnd. |
|
|
|
lVBEhwnd = FindWindow(VBE_CLASS_NAME, vbNullString) |
|
|
|
'prevent flickering of the screen |
|
'before posting messages to reset |
|
'the VBE window. |
|
|
|
LockWindowUpdate lVBEhwnd |
|
|
|
'do the same with the desktop in the background. |
|
|
|
SendMessage _ |
|
GetDesktopWindow, ByVal WM_SETREDRAW, ByVal 0&, 0& |
|
|
|
'stop and reset the VBE first to safely |
|
'proceed with our subclassing of xl. |
|
|
|
PostMessage _ |
|
lVBEhwnd, ByVal WM_USER + &HC44, ByVal &H30, ByVal 0& |
|
|
|
PostMessage _ |
|
lVBEhwnd, ByVal WM_USER + &HC44, ByVal &H33, ByVal 0& |
|
|
|
PostMessage _ |
|
lVBEhwnd, ByVal WM_USER + &HC44, ByVal &H83, ByVal 0& |
|
|
|
'run a one time timer and subclass xl |
|
'from the timer callback function. |
|
'if subclassing is not installed within |
|
'the timer callback,xl will crash ! |
|
|
|
SetTimer GetProp(GetDesktopWindow, "HWND") _ |
|
, 0&, 1, AddressOf TimerProc |
|
|
|
End Sub |
|
|
|
Sub UnSubClassExcel(hwnd As Long) |
|
|
|
'remove the subclass and cleanup. |
|
|
|
SetWindowLong hwnd, GWL_WNDPROC, lOldWinProc |
|
RemoveProp GetDesktopWindow, "HWND" |
|
lOldWinProc = 0 |
|
|
|
End Sub |
|
|
|
Private Function WindowProc _ |
|
(ByVal hwnd As Long, ByVal uMsg As Long, _ |
|
ByVal wParam As Long, ByVal lParam As Long) As Long |
|
|
|
'illustration example. |
|
|
|
On Error Resume Next |
|
|
|
Select Case uMsg |
|
|
|
'increment cell A1 by 1 |
|
'when moving the mouse |
|
'over the xl app title bar. |
|
|
|
Case WM_NCMOUSEMOVE |
|
Range("a1") = Range("a1") + 1 |
|
|
|
End Select |
|
|
|
'allow other msgs default processing. |
|
|
|
WindowProc = CallWindowProc _ |
|
(lOldWinProc, hwnd, uMsg, wParam, lParam) |
|
|
|
End Function |
|
|
|
|
|
Sub TimerProc(ByVal hwnd As Long, ByVal nIDEvent As Long, _ |
|
ByVal uElapse As Long, ByVal lpTimerFunc As Long) |
|
|
|
'we lost the hwnd stored in the lVBEhwnd var |
|
'after reseting the VBE so let's retrieve it again. |
|
|
|
lVBEhwnd = FindWindow(VBE_CLASS_NAME, vbNullString) |
|
|
|
'we no longer need the timer. |
|
|
|
KillTimer GetProp(GetDesktopWindow, "HWND"), 0& |
|
|
|
'allow back drawing on the desktop. |
|
|
|
SendMessage GetDesktopWindow, WM_SETREDRAW, ByVal 1, 0& |
|
|
|
'hide the VBE. |
|
|
|
ShowWindow lVBEhwnd, 0& |
|
|
|
'unlock the window update. |
|
|
|
LockWindowUpdate 0& |
|
|
|
'and at last we can now safely |
|
'subclass our target window. |
|
|
|
lOldWinProc = SetWindowLong _ |
|
(GetProp(GetDesktopWindow, "HWND"), _ |
|
GWL_WNDPROC, AddressOf WindowProc) |
|
|
|
End Sub |