Option Explicit
#If Win64 Then
Private Const NULL_PTR = 0^
#Else
Private Const NULL_PTR = 0&
#End If
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type POINTAPI
X As Long
Y As Long
End Type
#If VBA7 Then
#If Win64 Then
Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr
#Else
Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare PtrSafe Function GetWindowLong Lib "USER32.DLL" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
#End If
Private Declare PtrSafe Function SetParent Lib "user32" (ByVal hWndChild As LongPtr, ByVal hWndNewParent As LongPtr) As LongPtr
Private Declare PtrSafe Function GetParent Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function DrawMenuBar Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hwnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
Private Declare PtrSafe Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As LongPtr, hwnd As Any, Optional ByVal Msg As Long, Optional ByVal wParam As LongPtr, Optional ByVal lParam As LongPtr) As LongPtr
Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hwnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare PtrSafe Function GetWindowRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As Any) As Long
Private Declare PtrSafe Function SysMtr Lib "user32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function IsZoomed Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function SetActiveWindow Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function ClientToScreen Lib "user32" (ByVal hwnd As LongPtr, lpPoint As Any) As Long
Private Declare PtrSafe Function ScreenToClient Lib "user32" (ByVal hwnd As LongPtr, lpPoint As Any) As Long
Private Declare PtrSafe Function ShowWindowAsync Lib "user32" (ByVal hwnd As LongPtr, ByVal nCmdShow As Long) As Long
Private Declare PtrSafe Function IntersectRect Lib "user32" (lpDestRect As RECT, lpSrc1Rect As RECT, lpSrc2Rect As RECT) As Long
Private Declare PtrSafe Function IsRectEmpty Lib "user32" (lpRect As RECT) As Long
Private Declare PtrSafe Function SetWindowRgn Lib "user32" (ByVal hwnd As LongPtr, ByVal hRgn As LongPtr, ByVal bRedraw As Long) As Long
Private Declare PtrSafe Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As LongPtr
Private Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal hObject As LongPtr) 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 GetDeviceCaps Lib "gdi32" (ByVal hdc As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function AccessibleChildren Lib "oleacc" (ByVal paccContainer As Object, ByVal iChildStart As Long, ByVal cChildren As Long, ByRef rgvarChildren As Any, ByRef pcObtained As Long) As Long
Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "OLEACC.DLL" (ByVal hwnd As LongPtr, ByVal dwId As Long, ByVal riid As LongPtr, ppvObject As Any) As Long
Private Declare PtrSafe Function IIDFromString Lib "ole32.dll" (ByVal lpsz As LongPtr, ByVal lpiid As LongPtr) As LongPtr
Private Declare PtrSafe Function CoLockObjectExternal Lib "ole32.dll" (ByVal pUnk As IUnknown, ByVal fLock As Boolean, Optional ByVal fLastUnlockReleases As Boolean) As Long
Private Declare PtrSafe Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As LongPtr, ByVal oVft As LongPtr, ByVal cc As Long, ByVal vtReturn As Integer, ByVal cActuals As Long, ByRef prgvt As Integer, ByRef prgpvarg As LongPtr, ByRef pvargResult As Variant) As Long
Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByRef hwnd As LongPtr) As Long
#Else
Private Enum LongPtr
[_]
End Enum
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "USER32.DLL" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetParent Lib "user32" (ByVal hWndChild As LongPtr, ByVal hWndNewParent As LongPtr) As LongPtr
Private Declare Function GetParent Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hwnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As LongPtr, hwnd As Any, Optional ByVal Msg As Long, Optional ByVal wParam As LongPtr, Optional ByVal lParam As LongPtr) As LongPtr
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As Any) As Long
Private Declare Function SysMtr Lib "user32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
Private Declare Function IsZoomed Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare Function SetActiveWindow Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare Function ClientToScreen Lib "user32" (ByVal hwnd As LongPtr, lpPoint As Any) As Long
Private Declare Function ScreenToClient Lib "user32" (ByVal hwnd As LongPtr, lpPoint As Any) As Long
Private Declare Function ShowWindowAsync Lib "user32" (ByVal hwnd As LongPtr, ByVal nCmdShow As Long) As Long
Private Declare Function IntersectRect Lib "user32" (lpDestRect As RECT, lpSrc1Rect As RECT, lpSrc2Rect As RECT) As Long
Private Declare Function IsRectEmpty Lib "user32" (lpRect As RECT) As Long
Private Declare Function SetWindowRgn Lib "user32" (ByVal hwnd As LongPtr, ByVal hRgn As LongPtr, ByVal bRedraw As Long) As Long
Private Declare Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As LongPtr
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As LongPtr) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hdc As LongPtr) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As LongPtr, ByVal nIndex As Long) As Long
Private Declare Function AccessibleChildren Lib "oleacc" (ByVal paccContainer As Object, ByVal iChildStart As Long, ByVal cChildren As Long, ByRef rgvarChildren As Any, ByRef pcObtained As Long) As Long
Private Declare Function AccessibleObjectFromWindow Lib "OLEACC.DLL" (ByVal hwnd As LongPtr, ByVal dwId As Long, ByVal riid As LongPtr, ppvObject As Any) As Long
Private Declare Function IIDFromString Lib "ole32.dll" (ByVal lpsz As LongPtr, ByVal lpiid As LongPtr) As LongPtr
Private Declare Function CoLockObjectExternal Lib "ole32.dll" (ByVal pUnk As IUnknown, ByVal fLock As Boolean, Optional ByVal fLastUnlockReleases As Boolean) As Long
Private Declare Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As LongPtr, ByVal oVft As LongPtr, ByVal cc As Long, ByVal vtReturn As Integer, ByVal cActuals As Long, ByRef prgvt As Integer, ByRef prgpvarg As LongPtr, ByRef pvargResult As Variant) As Long
Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByRef hwnd As LongPtr) As Long
#End If
Private Type BUTTON_INFO
AnchorCell As Range
Width As Double
Height As Double
ParentHwnd As LongPtr
lpClickFunc As LongPtr
RelativeToParentWnd As Boolean
FloatAccrossAllSheets As Boolean
End Type
Private WithEvents CButton As MSForms.CommandButton
Private WithEvents Wb As Workbook
Private uBUTTON_INFO As BUTTON_INFO
Private hWndForm As LongPtr
Private lOffsetX As Long, lOffsetY As Long
Private bActivated As Boolean
Private Sub UserForm_Initialize()
Me.StartUpPosition = 0&
Call CoLockObjectExternal(Me, True)
Call IUnknown_GetWindow(Me, hWndForm)
End Sub
Private Sub UserForm_Activate()
Const WS_CAPTION = &HC00000: Const GWL_STYLE = (-16)
If bActivated Then Exit Sub
bActivated = True
Call SetWindowLong(hWndForm, GWL_STYLE, _
GetWindowLong(hWndForm, GWL_STYLE) And (Not WS_CAPTION))
Call DrawMenuBar(hWndForm)
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If bClosing = False Then Cancel = True: Exit Sub
Call CoLockObjectExternal(Me, False)
End Sub
Private Sub Wb_SheetActivate(ByVal Sh As Object)
If uBUTTON_INFO.FloatAccrossAllSheets Then Exit Sub
If uBUTTON_INFO.AnchorCell.Parent Is Sh Then
Me.Show vbModeless
Else
Me.Hide
End If
End Sub
Private Sub Wb_WindowResize(ByVal Wn As Window)
If Wn.hwnd = uBUTTON_INFO.ParentHwnd Then
Call SetWndDrawingRegion
End If
End Sub
Private Sub Wb_BeforeClose(Cancel As Boolean)
Unload Me
End Sub
Private Sub CButton_Click()
Call CallFuncByPointer(uBUTTON_INFO.lpClickFunc, CButton)
Call SetActiveWindow(uBUTTON_INFO.AnchorCell.Parent.Parent.Windows(1&).hwnd)
End Sub
Private Sub CButton_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Button = 2& Then
Call DisplayMenuPopUp
End If
End Sub
Private Sub UserForm_AddControl(ByVal Control As MSForms.Control)
Const GWL_EXSTYLE = (-20&), WS_EX_DLGMODALFRAME = &H1&, WS_EX_NOACTIVATE = &H8000000, _
SWP_SHOWWINDOW = &H40, SWP_HIDEWINDOW = &H80, SM_CXEDGE = 45&, SM_CXBORDER = 5&, SM_CXFRAME = 32&
Dim tTargetRect As RECT, tWndRect As RECT, pt As POINTAPI
Dim lX As Long, lY As Long, lW As Long, lH As Long, lOffset As Long, lShow As Long
With uBUTTON_INFO
Set .AnchorCell = Range(Split(Me.Tag, "|")(0&))
.Width = CDbl(Split(Me.Tag, "|")(1&))
.Height = CDbl(Split(Me.Tag, "|")(2&))
.ParentHwnd = CLngPtr(Split(Me.Tag, "|")(3&))
.lpClickFunc = CLngPtr(Split(Me.Tag, "|")(4&))
.RelativeToParentWnd = CBool(Split(Me.Tag, "|")(5&))
.FloatAccrossAllSheets = CBool(Split(Me.Tag, "|")(6&))
End With
Set CButton = Control
With CButton
.Left = 0&: .Top = 0&
.Width = uBUTTON_INFO.Width: .Height = uBUTTON_INFO.Height
End With
Call SetParent(hWndForm, uBUTTON_INFO.ParentHwnd)
Call SetWindowLong(hWndForm, GWL_EXSTYLE, _
GetWindowLong(hWndForm, GWL_EXSTYLE) And (Not WS_EX_DLGMODALFRAME) Or WS_EX_NOACTIVATE)
Call GetWindowRect(uBUTTON_INFO.ParentHwnd, tWndRect)
If IsZoomed(uBUTTON_INFO.ParentHwnd) Then
lOffset = SysMtr(SM_CXBORDER) + SysMtr(SM_CXEDGE) + SysMtr(SM_CXFRAME)
End If
With uBUTTON_INFO.AnchorCell.Parent.Parent.Windows(1&).ActivePane
lW = PTtoPX(uBUTTON_INFO.Width, False): lH = PTtoPX(uBUTTON_INFO.Height, True)
End With
tTargetRect = GetRangeRect(uBUTTON_INFO.AnchorCell)
With tTargetRect
lX = .Left - tWndRect.Left - lOffset: lY = .Top - tWndRect.Top - lOffset
End With
If uBUTTON_INFO.FloatAccrossAllSheets = False And Not (ActiveSheet Is uBUTTON_INFO.AnchorCell.Parent) Then
lShow = SWP_HIDEWINDOW
Call ShowWindowAsync(hWndForm, 0&)
Else
lShow = SWP_SHOWWINDOW
End If
Call SetWindowPos(hWndForm, NULL_PTR, lX, lY, lW, lH, SWP_SHOWWINDOW)
If uBUTTON_INFO.RelativeToParentWnd Then
pt.X = lX: pt.Y = lY
Call ClientToScreen(uBUTTON_INFO.AnchorCell.Parent.Parent.Windows(1&).hwnd, pt)
lOffsetX = tWndRect.Right - pt.X: lOffsetY = tWndRect.Bottom - pt.Y
End If
Set Wb = ThisWorkbook
DoEvents
Call SetWndDrawingRegion
End Sub
Private Sub SetWndDrawingRegion()
Const SWP_SHOWWINDOW = &H40, SWP_NOSIZE = &H1
Const SM_CXVSCROLL = 2&, SM_CXBORDER = 5&, SM_CYDLGFRAME = 8&
Dim tParentRect As RECT, tFormRect As RECT, tVisibleRect As RECT, tDesRect As RECT
Dim tScrBarHRect As RECT
Dim hScrollBarH As LongPtr, hScrollBarV As LongPtr
Dim hIntersectRgn As LongPtr
Dim lX As Long, lY As Long
Call GetWindowRect(uBUTTON_INFO.ParentHwnd, tParentRect)
If uBUTTON_INFO.RelativeToParentWnd Then
With tParentRect
lX = (.Right - .Left) - lOffsetX: lY = (.Bottom - .Top) - lOffsetY
End With
Call SetWindowPos(hWndForm, NULL_PTR, lX, lY, 0&, 0&, SWP_SHOWWINDOW + SWP_NOSIZE)
End If
Call GetWindowRect(hWndForm, tFormRect)
hScrollBarH = FindWindowEx(uBUTTON_INFO.ParentHwnd, NULL_PTR, "XLDESK", vbNullString)
hScrollBarH = FindWindowEx(hScrollBarH, NULL_PTR, "EXCEL7", vbNullString)
hScrollBarH = FindWindowEx(hScrollBarH, NULL_PTR, vbNullString, "Horizontal")
Call GetWindowRect(hScrollBarH, tScrBarHRect)
If IsRectEmpty(tScrBarHRect) Then
hScrollBarH = FindWindowEx(GetParent(hScrollBarH), hScrollBarH, vbNullString, "Horizontal")
Call GetWindowRect(hScrollBarH, tScrBarHRect)
End If
tVisibleRect = GetRangeRect(uBUTTON_INFO.AnchorCell.Parent.Parent.Windows(1&).VisibleRange)
With tVisibleRect
If uBUTTON_INFO.AnchorCell.Parent.Parent.Windows(1&).DisplayVerticalScrollBar Then
.Right = tParentRect.Right - (SysMtr(SM_CXVSCROLL) + SysMtr(SM_CXBORDER) + 2& * SysMtr(SM_CYDLGFRAME))
Else
.Right = tParentRect.Right
End If
If uBUTTON_INFO.AnchorCell.Parent.Parent.Windows(1&).DisplayHorizontalScrollBar Then
.Bottom = tScrBarHRect.Top - 7&
Else
If GetFootSheetTop Then
.Bottom = GetFootSheetTop
End If
End If
End With
Call IntersectRect(tDesRect, tFormRect, tVisibleRect)
With tDesRect
Call ScreenToClient(hWndForm, .Left)
Call ScreenToClient(hWndForm, .Right)
hIntersectRgn = CreateRectRgn(.Left, .Top, .Right, .Bottom)
End With
If SetWindowRgn(hWndForm, hIntersectRgn, True) = 0 Then
Call DeleteObject(hIntersectRgn)
End If
End Sub
Private Function ScreenDPI(ByVal bVert As Boolean) As Long
Const LOGPIXELSX As Long = 88&, LOGPIXELSY As Long = 90&
Static lDPI(1) As Long, hdc As LongPtr
If lDPI(0&) = 0& Then
hdc = GetDC(NULL_PTR)
lDPI(0&) = GetDeviceCaps(hdc, LOGPIXELSX)
lDPI(1&) = GetDeviceCaps(hdc, LOGPIXELSY)
hdc = ReleaseDC(NULL_PTR, hdc)
End If
ScreenDPI = lDPI(Abs(bVert))
End Function
Private Function PTtoPX(Points As Double, bVert As Boolean) As Long
Const POINTS_PER_INCH = 72&
PTtoPX = Points * ScreenDPI(bVert) / POINTS_PER_INCH
End Function
Private Function GetRangeRect(ByVal Rng As Range) As RECT
Dim oPane As Pane
Set oPane = Rng.Parent.Parent.Windows(1&).ActivePane
With GetRangeRect
.Left = oPane.PointsToScreenPixelsX(Rng.Left - 1&)
.Top = oPane.PointsToScreenPixelsY(Rng.Top)
.Right = oPane.PointsToScreenPixelsX(Rng.Left + Rng.Width)
.Bottom = oPane.PointsToScreenPixelsY(Rng.Top + Rng.Height)
End With
End Function
Private Function GetFootSheetTop() As Long
Const S_OK = &H0&, OBJID_CLIENT = -4&
Const ID_ACCESSIBLE As String = "{618736E0-3C3D-11CF-810C-00AA00389B71}"
Dim tGUID(0 To 3) As Long
Dim oIAc As IAccessible
Dim vTmp As Variant
Dim hStatusBar As LongPtr, hExcel7 As LongPtr
Dim lTop1 As Long, lTop2 As Long
hStatusBar = FindWindowEx(uBUTTON_INFO.ParentHwnd, NULL_PTR, "EXCEL2", vbNullString)
hStatusBar = FindWindowEx(hStatusBar, NULL_PTR, vbNullString, "Status Bar")
If IIDFromString(StrPtr(ID_ACCESSIBLE), VarPtr(tGUID(0))) = S_OK Then
If AccessibleObjectFromWindow(hStatusBar, OBJID_CLIENT, VarPtr(tGUID(0&)), oIAc) = S_OK Then
oIAc.accLocation 0&, lTop1, 0&, 0&, 0&
End If
End If
Set oIAc = Nothing
hExcel7 = FindWindowEx(uBUTTON_INFO.ParentHwnd, NULL_PTR, "XLDESK", vbNullString)
hExcel7 = FindWindowEx(hExcel7, NULL_PTR, "EXCEL7", vbNullString)
If IIDFromString(StrPtr(ID_ACCESSIBLE), VarPtr(tGUID(0))) = S_OK Then
If AccessibleObjectFromWindow(hExcel7, OBJID_CLIENT, VarPtr(tGUID(0&)), oIAc) = S_OK Then
Set vTmp = oIAc
If AccessibleChildren(vTmp, 5&, 1&, vTmp, 0&) = S_OK Then
On Error Resume Next
vTmp.accLocation 0&, lTop2, 0&, 0&, 0&
On Error GoTo 0
End If
Select Case True
Case lTop1 <> 0& And lTop2 = 0&
GetFootSheetTop = lTop1
Case lTop1 = 0& And lTop2 <> 0&
GetFootSheetTop = lTop2
Case lTop1 <> 0& And lTop2 <> 0&
GetFootSheetTop = Application.Min(lTop1, lTop2)
End Select
End If
End If
End Function
Private Function CallFuncByPointer(ClickEventPtr As LongPtr, Button As MSForms.CommandButton) As Variant
Const CC_STDCALL = 4&
Dim vParams(0&) As Variant
Dim vParamPtr(0& To 0&) As LongPtr
Dim vParamType(0& To 0&) As Integer
vParams(0&) = ObjPtr(Button)
vParamPtr(0&) = VarPtr(vParams(0))
vParamType(0&) = VarType(vParams(0))
Call DispCallFunc(NULL_PTR, ClickEventPtr, _
CC_STDCALL, vbEmpty, 1&, vParamType(0&), vParamPtr(0&), CallFuncByPointer)
End Function
Private Sub DisplayMenuPopUp()
On Error Resume Next
Application.CommandBars("MyPopUpMenu").Delete
On Error GoTo 0
With Application.CommandBars.Add(Name:="MyPopUpMenu", Position:=msoBarPopup, MenuBar:=False, Temporary:=True)
With .Controls.Add(Type:=msoControlButton)
.Caption = "&Remove Me"
.BeginGroup = True
.State = msoButtonDown
.FaceId = 358&
.OnAction = "'Unload_Macro " & Chr(34&) & ObjPtr(Me) & Chr(34&) & "'"
End With
End With
Application.CommandBars("MyPopUpMenu").ShowPopup
End Sub