Option Explicit
Private Enum eTIP_POS
BottomRight = 0
BottomLeft = 1
TopRight = 2
TopLeft = 4
MousePos = 8
End Enum
Private Enum eSTYLE
TTS_ALWAYSTIP = &H1
TTS_NOPREFIX = &H2
TTS_NOANIMATE = &H10
TTS_NOFADE = &H20
TTS_BALLOON = &H40
TTS_CLOSE = &H80
End Enum
Private Enum eICON
TTI_NONE = 0
TTI_INFO = 1
TTI_WARNING = 2
TTI_ERROR = 3
End Enum
Private Type ToolTipData
cbSize As Long
Style As eSTYLE
BackColor As Long
SystemInfoBackColor As Boolean 'overrides BackColor
TextColor As Long
SystemInfoTextColor As Boolean 'overrides TextColor
Title As String * 64
Icon As eICON
Text As String * 1024
DelayTime As Long 'in Secs
BeepSound As Boolean
Position As eTIP_POS
End Type
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Type Rect
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type ToolInfo
cbSize As Long
uFlags As Long
#If Win64 Then
hwnd As LongLong
uId As LongLong
cRect As Rect
hinst As LongLong
#Else
hwnd As Long
uId As Long
cRect As Rect
hinst As Long
#End If
lpszText As String
End Type
Private Type InitCommonControlsEx
Size As Long
ICC As Long
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
#If VBA7 Then
#If Win64 Then
Private Declare PtrSafe Function AccessibleObjectFromPoint Lib "oleacc" (ByVal arg1 As LongLong, ppacc As Any, pvarChild As Variant) As Long
Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Else
Private Declare PtrSafe Function AccessibleObjectFromPoint Lib "oleacc" (ByVal lX As Long, ByVal lY As Long, ppacc As IAccessible, pvarChild As Variant) As Long
Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
#End If
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As LongPtr
Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
Private Declare PtrSafe Function SendMessageAny Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr
Private Declare PtrSafe Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As LongPtr, ByVal hMenu As LongPtr, ByVal hInstance As LongPtr, lpParam As Any) As LongPtr
Private Declare PtrSafe Function DestroyWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal nCmdShow As Long) As Long
Private Declare PtrSafe Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal hData As LongPtr) As Long
Private Declare PtrSafe Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
Private Declare PtrSafe Function RemoveProp Lib "user32" Alias "RemovePropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
Private Declare PtrSafe Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" (ByVal Punk As stdole.IUnknown, ByRef riidEvent As GUID, ByVal fConnect As Long, ByVal PunkTarget As stdole.IUnknown, ByRef pdwCookie As Long, Optional ByVal ppcpOut As LongPtr) As Long
Private Declare PtrSafe Function InitCommonControlsEx Lib "comctl32.dll" (iccex As InitCommonControlsEx) As Long
Private Declare PtrSafe Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As LongPtr) As Long
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function FrameRect Lib "user32" (ByVal hdc As LongPtr, lpRect As Rect, ByVal hBrush As LongPtr) As Long
Private Declare PtrSafe Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long) As LongPtr
Private Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal hObject As LongPtr) As Long
Private Declare PtrSafe Function GetClientRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As Rect) As Long
Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hdc As LongPtr) As Long
Private Declare PtrSafe Function TranslateColor Lib "oleAut32.dll" Alias "OleTranslateColor" (ByVal clr As OLE_COLOR, ByVal palet As LongPtr, Col As Long) As Long
Private hForm As LongPtr, hToolTip As LongPtr
#Else
Private Declare Function AccessibleObjectFromPoint Lib "oleacc" (ByVal lX As Long, ByVal lY As Long, ppacc As IAccessible, pvarChild As Variant) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SendMessageAny Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, lpParam As Any) As Long
Private Declare Function DestroyWindow Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) 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 GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
Private Declare Function RemoveProp Lib "user32" Alias "RemovePropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
Private Declare Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" (ByVal punk As stdole.IUnknown, ByRef riidEvent As GUID, ByVal fConnect As Long, ByVal punkTarget As stdole.IUnknown, ByRef pdwCookie As Long, Optional ByVal ppcpOut As Long) As Long
Private Declare Function InitCommonControlsEx Lib "comctl32.dll" (iccex As InitCommonControlsEx) As Long
Private Declare Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function FrameRect Lib "user32" (ByVal hdc As Long, lpRect As Rect, ByVal hBrush As Long) As Long
Private Declare Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As Rect) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Private Declare Function TranslateColor Lib "oleAut32.dll" Alias "OleTranslateColor" (ByVal clr As OLE_COLOR, ByVal palet As Long, Col As Long) As Long
Private hForm As Long, hToolTip As Long
#End If
Private WithEvents oForm As MSForms.UserForm
Private bFormUnloading As Boolean
Private bNextUpdate As Boolean
Private bTimedOut As Boolean
Private bNoTimedOut As Boolean
Private tGUID As GUID, tToolInfo As ToolInfo
Private oCtrl As Object
Private lCookie As Long
Private lStyle As eSTYLE, lIcon As eICON, lPosition As eTIP_POS
Private sText As String, sTitle As String
Private lBkColor As Long, lTextColor As Long
Private bSysBkColor As Boolean, bSysTextColor As Boolean
Private lTimeOut As Long
Private bBeep As Boolean
'_________________________________________Class Public Methods__________________________________________________
#If Win64 Then
Public Sub AddTo(ByVal Ctrl As MSForms.Control, ByVal DataPtr As LongLong)
#Else
Public Sub AddTo(ByVal Ctrl As MSForms.Control, ByVal DataPtr As Long)
#End If
Const S_OK = 0
Dim tTTipData As ToolTipData
Set oCtrl = Ctrl
Set oForm = GetUserFormObject(Ctrl)
Call IUnknown_GetWindow(oForm, VarPtr(hForm))
With tGUID
.Data1 = &H20400
.Data4(0) = &HC0
.Data4(7) = &H46
End With
If ConnectToConnectionPoint(Me, tGUID, True, Ctrl, lCookie) = S_OK Then
Call CopyMemory(ByVal tTTipData, ByVal DataPtr, LenB(tTTipData))
With tTTipData
sText = Left(.Text, InStr(1, .Text, vbNullChar) - 1)
sTitle = Left(.Title, InStr(1, .Title, vbNullChar) - 1)
lStyle = .Style
lIcon = .Icon
Call TranslateColor(.BackColor, 0, lBkColor)
lTextColor = .TextColor
bSysBkColor = .SystemInfoBackColor
bSysTextColor = .SystemInfoTextColor
lTimeOut = Int(.DelayTime)
If lTimeOut <= 0 Then bNoTimedOut = True
bBeep = .BeepSound
lPosition = .Position
End With
Call CreateToolTip
Else
Err.Raise Number:=vbObjectError + 513, Description:="Unable to register the mouse event listener."
End If
End Sub
Public Sub Remove()
If FindWindow("tooltips_class32", CStr(hForm)) Then
Call ConnectToConnectionPoint(Nothing, tGUID, False, oCtrl, lCookie)
Call DestroyWindow(GetProp(Application.hwnd, CStr(hForm) & "ToolTip"))
Call RemoveProp(Application.hwnd, CStr(hForm) & "ToolTip")
End If
bFormUnloading = True
Set oCtrl = Nothing
Set oForm = Nothing
End Sub
Public Sub DO_NOT_USE(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
'Attribute DO_NOT_USE.VB_UserMemId = -606
Call RetrieveControlUnderMousePointer(oCtrl)
End Sub
'__________________________________________Class Private Routines__________________________________________________
Private Sub RetrieveControlUnderMousePointer(ByVal Ctrl As MSForms.Control)
Const CHILDID_SELF = &H0&
Static bDoLooping As Boolean
Dim tCurPos As POINTAPI, oCurAcc As IAccessible, oPrevAcc As IAccessible
Dim px1 As Long, py1 As Long, pw1 As Long, ph1 As Long
Dim px2 As Long, py2 As Long, pw2 As Long, ph2 As Long
Dim sCurAccLocation As String, sPrevAccLocation As String
Dim t As Single
t = Timer
If bDoLooping Then Exit Sub
Do
If bNoTimedOut = False Then
If Int(Timer - t) >= lTimeOut Or bTimedOut = True Then bTimedOut = True: Exit Do
End If
bDoLooping = True
Call GetCursorPos(tCurPos)
#If Win64 Then
Dim ptr As LongLong
Call CopyMemory(ptr, tCurPos, LenB(tCurPos))
Call AccessibleObjectFromPoint(ptr, oCurAcc, CHILDID_SELF)
#Else
Call AccessibleObjectFromPoint(tCurPos.X, tCurPos.Y, oCurAcc, CHILDID_SELF)
#End If
Call oCurAcc.accLocation(px1, py1, pw1, ph1, CHILDID_SELF)
If Not oPrevAcc Is Nothing Then
Call oPrevAcc.accLocation(px2, py2, pw2, ph2, CHILDID_SELF)
End If
sCurAccLocation = CStr(px1) & CStr(py1) & CStr(pw1) & CStr(ph1)
sPrevAccLocation = CStr(px2) & CStr(py2) & CStr(pw2) & CStr(ph2)
If oPrevAcc Is Nothing And sCurAccLocation <> sPrevAccLocation Then
Call UpdateToolTip(px1, py1, pw1, ph1, tCurPos)
End If
Set oPrevAcc = Ctrl
Call oPrevAcc.accLocation(px2, py2, pw2, ph2, CHILDID_SELF)
sPrevAccLocation = CStr(px2) & CStr(py2) & CStr(pw2) & CStr(ph2)
DoEvents
Loop Until sCurAccLocation <> sPrevAccLocation Or bFormUnloading
bDoLooping = False
Call HideToolTip
End Sub
Private Function GetUserFormObject(ByVal Ctrl As MSForms.Control) As Object
Dim oTemp As Object
Set oTemp = Ctrl.Parent
Do While TypeOf oTemp Is MSForms.Control
Set oTemp = oTemp.Parent
DoEvents
Loop
Set GetUserFormObject = oTemp
End Function
Private Sub CreateToolTip()
Const WS_POPUP = &H80000000
Const WS_EX_NOACTIVATE = &H8000000
Const CW_USEDEFAULT = &H80000000
Const ICC_WIN95_CLASSES = &HFF
Const ICC_TAB_CLASSES = &H8
Const WM_USER = &H400
Const TTF_TRACK = &H20
Const TTF_TRANSPARENT = &H100
Const TTM_ACTIVATE = (WM_USER + 1)
Const TTM_ADDTOOL = (WM_USER + 4)
Dim tIccex As InitCommonControlsEx
If FindWindow("tooltips_class32", CStr(hForm)) = 0 Then
If hToolTip = 0 Then
With tIccex
.Size = LenB(tIccex)
.ICC = ICC_TAB_CLASSES
End With
If InitCommonControlsEx(tIccex) Then
hToolTip = CreateWindowEx(WS_EX_NOACTIVATE, "tooltips_class32", CStr(hForm), lStyle + WS_POPUP, _
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(vbNullString), 0)
With tToolInfo
.cbSize = LenB(tToolInfo)
.hwnd = hForm
.uFlags = TTF_TRACK Or TTF_TRANSPARENT
End With
Call SendMessageAny(hToolTip, TTM_ADDTOOL, 0, tToolInfo)
Call SendMessage(hToolTip, TTM_ACTIVATE, False, ByVal 0)
Else
Err.Raise Number:=vbObjectError + 513, Description:="Unable to load common controls."
End If
End If
End If
End Sub
Private Sub UpdateToolTip(ByVal pX As Long, ByVal pY As Long, ByVal pw As Long, ByVal ph As Long, ByRef tP As POINTAPI)
Const WM_USER = &H400
Const TTF_TRACK = &H20
Const TTF_ABSOLUTE = &H80
Const TTF_TRANSPARENT = &H100
Const TTM_ACTIVATE = (WM_USER + 1)
Const TTM_SETDELAYTIME = (WM_USER + 3)
Const TTM_UPDATETIPTEXT = (WM_USER + 12)
Const TTM_TRACKACTIVATE = (WM_USER + 17)
Const TTM_TRACKPOSITION = (WM_USER + 18)
Const TTM_UPDATE = (WM_USER + 29)
Const TTM_SETTITLEA = (WM_USER + 32)
Const TTM_SETMAXTIPWIDTH = (WM_USER + 24)
Const TTM_SETTIPTEXTCOLOR = (WM_USER + 20)
Const TTM_SETTIPBKCOLOR = (WM_USER + 19)
Const COLOR_INFOBK = 24
Const COLOR_INFOTEXT = 23
Const GWL_STYLE = (-16)
#If Win64 Then
Dim hBrush As LongLong, hdc As LongLong
#Else
Dim hBrush As Long, hdc As Long
#End If
Dim tClientRect As Rect
Dim X As Integer, Y As Integer
hToolTip = FindWindow("tooltips_class32", CStr(hForm))
If hToolTip Then
Call HideToolTip
Select Case lPosition
Case BottomRight
X = CInt(pX + pw - 5): Y = CInt(pY + ph - 5)
Case BottomLeft
X = CInt(pX + 5): Y = CInt(pY + ph - 5)
Case TopRight
X = CInt(pX + pw - 5): Y = CInt(pY + 5)
Case TopLeft
X = CInt(pX + 5): Y = CInt(pY + 5)
Case MousePos
Y = tP.Y: X = tP.X
End Select
With tToolInfo
.cbSize = LenB(tToolInfo)
.hwnd = hForm
.uFlags = TTF_TRACK Or TTF_TRANSPARENT
.lpszText = sText
End With
If lStyle And TTS_BALLOON Then
Call SetWindowLong(hToolTip, GWL_STYLE, lStyle)
Else
Call SetWindowLong(hToolTip, GWL_STYLE, lStyle And Not TTS_BALLOON)
End If
Call SendMessage(hToolTip, TTM_SETMAXTIPWIDTH, 0&, 5000)
Call SendMessage(hToolTip, TTM_SETTIPTEXTCOLOR, IIf(bSysTextColor, GetSysColor(COLOR_INFOTEXT), lTextColor), 0)
Call SendMessage(hToolTip, TTM_SETTIPBKCOLOR, IIf(bSysBkColor, GetSysColor(COLOR_INFOBK), lBkColor), 0)
Call SendMessageAny(hToolTip, TTM_UPDATETIPTEXT, 0, tToolInfo)
Call SendMessageAny(hToolTip, TTM_SETTITLEA, lIcon, ByVal sTitle)
Call SendMessage(hToolTip, TTM_UPDATE, ByVal 0, ByVal 0)
Call SendMessageAny(hToolTip, TTM_TRACKACTIVATE, True, tToolInfo)
Call SendMessageAny(hToolTip, TTM_TRACKPOSITION, ByVal 0&, ByVal MakeDWord(X, Y))
Call SendMessage(hToolTip, TTM_ACTIVATE, True, ByVal 0)
Call GetClientRect(hToolTip, tClientRect)
hdc = GetDC(hToolTip)
hBrush = CreateSolidBrush(lTextColor)
Call FrameRect(hdc, tClientRect, hBrush)
Call DeleteObject(hBrush)
Call ReleaseDC(hToolTip, hdc)
Call SetProp(Application.hwnd, CStr(hForm) & "ToolTip", hToolTip)
If bBeep Then Beep
End If
If bNextUpdate = False Then
bNextUpdate = True
UpdateToolTip pX, pY, pw, ph, tP
End If
End Sub
Private Sub HideToolTip()
Call ShowWindow(GetProp(Application.hwnd, CStr(hForm) & "ToolTip"), 0)
End Sub
Private Function MakeDWord(loword As Integer, hiword As Integer) As Long
MakeDWord = (hiword * &H10000) Or (loword And &HFFFF&)
End Function
Private Sub oForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Const WM_USER = &H400
Const TTM_ACTIVATE = (WM_USER + 1)
Call HideToolTip
bNextUpdate = False
Call SendMessage(hToolTip, TTM_ACTIVATE, 0, ByVal 0)
bTimedOut = False
End Sub