home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 6_2008-2009.ISO / data / zips / Time_Clock211986782008.psc / ccXPButton.ctl < prev    next >
Text File  |  2004-12-23  |  46KB  |  970 lines

  1. VERSION 5.00
  2. Begin VB.UserControl ccXPButton 
  3.    AutoRedraw      =   -1  'True
  4.    ClientHeight    =   405
  5.    ClientLeft      =   0
  6.    ClientTop       =   0
  7.    ClientWidth     =   1230
  8.    DefaultCancel   =   -1  'True
  9.    Enabled         =   0   'False
  10.    BeginProperty Font 
  11.       Name            =   "Verdana"
  12.       Size            =   8.25
  13.       Charset         =   0
  14.       Weight          =   400
  15.       Underline       =   0   'False
  16.       Italic          =   0   'False
  17.       Strikethrough   =   0   'False
  18.    EndProperty
  19.    ScaleHeight     =   27
  20.    ScaleMode       =   3  'Pixel
  21.    ScaleWidth      =   82
  22. End
  23. Attribute VB_Name = "ccXPButton"
  24. Attribute VB_GlobalNameSpace = False
  25. Attribute VB_Creatable = True
  26. Attribute VB_PredeclaredId = False
  27. Attribute VB_Exposed = False
  28. '-------------------------------------------------------------------------------------------------
  29. ' Module    : ccXPButton
  30. ' Updated   : Nov 29 2004
  31. ' Author    : Chris Cochran
  32. ' Purpose   : My goal with this button is simple: to create a efficient and reliable XPButton
  33. '             that is appropriate for 99% of the apps I write, a single line button without all
  34. '             the overhead of multiple visual styles. I painstakingly tested this control to
  35. '             ensure it never draws twice unessasarily, or freaks when the user doesn't release
  36. '             the mouse button when expected, or when the parent form loses the Windows focus.
  37. '             If all you want is an efficient XP button that works solid, this one may be for you.
  38. '
  39. ' Credits   : The subclassing routines included below are the work of Paul Caton.
  40. '
  41. ' Web Post  : http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=57148&lngWId=1
  42. '-------------------------------------------------------------------------------------------------
  43. Option Explicit
  44.  
  45. '//Subclasser declarations
  46. Private Enum eMsgWhen
  47.   MSG_AFTER = 1                                                                         'Message calls back after the original (previous) WndProc
  48.   MSG_BEFORE = 2                                                                        'Message calls back before the original (previous) WndProc
  49.   MSG_BEFORE_AND_AFTER = MSG_AFTER Or MSG_BEFORE                                        'Message calls back before and after the original (previous) WndProc
  50. End Enum
  51. Private Type tSubData                                                                   'Subclass data type
  52.   hWnd                               As Long                                            'Handle of the window being subclassed
  53.   nAddrSub                           As Long                                            'The address of our new WndProc (allocated memory).
  54.   nAddrOrig                          As Long                                            'The address of the pre-existing WndProc
  55.   nMsgCntA                           As Long                                            'Msg after table entry count
  56.   nMsgCntB                           As Long                                            'Msg before table entry count
  57.   aMsgTblA()                         As Long                                            'Msg after table array
  58.   aMsgTblB()                         As Long                                            'Msg Before table array
  59. End Type
  60. Private sc_aSubData()                As tSubData                                        'Subclass data array
  61. Private Const ALL_MESSAGES           As Long = -1                                       'All messages added or deleted
  62. Private Const GMEM_FIXED             As Long = 0                                        'Fixed memory GlobalAlloc flag
  63. Private Const GWL_WNDPROC            As Long = -4                                       'Get/SetWindow offset to the WndProc procedure address
  64. Private Const PATCH_04               As Long = 88                                       'Table B (before) address patch offset
  65. Private Const PATCH_05               As Long = 93                                       'Table B (before) entry count patch offset
  66. Private Const PATCH_08               As Long = 132                                      'Table A (after) address patch offset
  67. Private Const PATCH_09               As Long = 137                                      'Table A (after) entry count patch offset
  68. Private Declare Sub RtlMoveMemory Lib "kernel32" (Destination As Any, Source As Any, ByVal Length As Long)
  69. Private Declare Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As Long
  70. Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
  71. Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
  72. Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
  73. Private Declare Function SetWindowLongA Lib "user32" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
  74.  
  75. '//Mouse tracking declares
  76. Private Declare Function TrackMouseEvent Lib "user32" (lpEventTrack As TRACKMOUSEEVENT_STRUCT) As Long
  77. Private Enum TRACKMOUSEEVENT_FLAGS
  78.     TME_HOVER = &H1&
  79.     TME_LEAVE = &H2&
  80.     TME_QUERY = &H40000000
  81.     TME_CANCEL = &H80000000
  82. End Enum
  83. Private Type TRACKMOUSEEVENT_STRUCT
  84.     cbSize                              As Long
  85.     dwFlags                             As TRACKMOUSEEVENT_FLAGS
  86.     hwndTrack                           As Long
  87.     dwHoverTime                         As Long
  88. End Type
  89. Private Const WM_MOUSELEAVE             As Long = &H2A3
  90.  
  91. '//DrawText declares
  92. Private Type RECT
  93.     Left As Long
  94.     Top As Long
  95.     Right As Long
  96.     Bottom As Long
  97. End Type
  98. Private Const DT_VCENTER                As Long = &H4
  99. Private Const DT_SINGLELINE             As Long = &H20
  100. Private Const DT_FLAGS                  As Long = DT_VCENTER + DT_SINGLELINE
  101. Private Const DT_CENTER                 As Long = &H1
  102. Private Declare Function DrawText Lib "user32" Alias "DrawTextA" (ByVal hdc As Long, ByVal lpStr As String, ByVal nCount As Long, lpRect As RECT, ByVal wFormat As Long) As Long
  103.  
  104. '//Gradient Fill Declares
  105. Private Declare Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long) As Long
  106. Private Type POINT
  107.    x As Long
  108.    y As Long
  109. End Type
  110. Private Type RGBColor
  111.     r As Single
  112.     G As Single
  113.     B As Single
  114. End Type
  115.  
  116. '//Misc and multi-use declares
  117. Private Const WM_NCACTIVATE As Long = &H86
  118. Private Const WM_ACTIVATE   As Long = &H6
  119. Private Declare Function SetRect Lib "user32" (lpRect As RECT, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
  120. Private Declare Function FillRect Lib "user32" (ByVal hdc As Long, lpRect As RECT, ByVal hBrush As Long) As Long
  121. Private Declare Function SetPixel Lib "gdi32.dll" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long) As Long
  122. Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
  123. Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
  124. Private Declare Function MoveToEx Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, lpPoint As POINT) As Long
  125. Private Declare Function LineTo Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long
  126. Private Declare Function CreatePen Lib "gdi32" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long
  127. Private Declare Function ClientToScreen Lib "user32" (ByVal hWnd As Long, lpPoint As POINT) As Long
  128. Private Declare Function SetCursorPos Lib "user32" (ByVal x As Long, ByVal y As Long) As Long
  129. Private Declare Function DrawFocusRect Lib "user32" (ByVal hdc As Long, lpRect As RECT) As Long
  130. Private Declare Function CreateRoundRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long) As Long
  131. Private Declare Function SetWindowRgn Lib "user32" (ByVal hWnd As Long, ByVal hRgn As Long, ByVal bRedraw As Long) As Long
  132.  
  133. '//Button states
  134. Private Enum enumStates
  135.     eDISABLE = 0
  136.     eIDLE = 1
  137.     eFOCUS = 2
  138.     eHOT = 3
  139.     eDOWN = 4
  140. End Enum
  141.  
  142. Public Enum WindowState
  143.     InActive = 0
  144.     Active = 1
  145. End Enum
  146.  
  147. '//Button colors
  148. Private Type typeColors
  149.     cBorders(0 To 4)        As Long
  150.     cTopLine1(0 To 4)       As Long
  151.     cTopLine2(0 To 4)       As Long
  152.     cBottomLine1(0 To 4)    As Long
  153.     cBottomLine2(0 To 4)    As Long
  154.     cCornerPixel1(0 To 4)   As Long
  155.     cCornerPixel2(0 To 4)   As Long
  156.     cCornerPixel3(0 To 4)   As Long
  157.     cSideGradTop(1 To 3)    As Long
  158.     cSideGradBottom(1 To 3) As Long
  159. End Type
  160.  
  161. '//Public Events
  162. Public Event Click()
  163. Public Event DblClick()
  164. Public Event MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
  165. Public Event FormActivate(State As WindowState)
  166.  
  167. '//Private variables
  168. Private iDownButton         As Integer '------- Down mouse button (for DblClick event)
  169. Private bSkipDrawing        As Boolean '------- Pauses drawing when needed
  170. Private bButtonIsDown       As Boolean '------- Tracks button down state
  171. Private bHasFocus           As Boolean '------- Tracks button focus state
  172. Private bMouseInControl     As Boolean '------- Tracks when mouse is in or out of the button
  173. Private tColors             As typeColors '---- Enum declare for typeColors
  174. Private bParentActive       As Boolean '------- Tracks when parent form has the Windows focus
  175. Private bSpaceBarIsDown     As Boolean '------- Tracks state of spacebar for KeyUp/Down events
  176. Private bMouseButtonIsDown  As Boolean '------- Tracks state of mousebutton for KeyUp/Down events
  177. Private bDisplayAsDefault   As Boolean '------- USed for ambient default property changes
  178. Private lParentHwnd         As Long '---------- Stores the parents window handle
  179. Private eSTATE              As enumStates '---- Enum declare for enumStates
  180.  
  181. '//Propbag variables
  182. Private pHWND               As Long
  183. Private pCAPTION            As String
  184. Private pENABLED            As Boolean
  185. Private pFORECOLOR          As OLE_COLOR
  186. Private pFOCUSRECT          As Boolean
  187. Private WithEvents pFONT    As StdFont
  188. Attribute pFONT.VB_VarHelpID = -1
  189.  
  190. Private Sub DrawButton(ByVal State As enumStates)
  191. On Error Resume Next
  192. Dim lw          As Long
  193. Dim lh          As Long
  194. Dim lHdc        As Long
  195. Dim r           As RECT
  196. Dim hRgn        As Long
  197.  
  198. If bSkipDrawing Then Exit Sub Else eSTATE = State '--------------------- Bolt if desired
  199.  
  200. With UserControl: lw = .ScaleWidth: lh = .ScaleHeight: .Cls: End With
  201. lHdc = UserControl.hdc
  202.  
  203. With tColors
  204.     LineApi 3, 0, lw - 3, 0, .cBorders(eSTATE) '------------------------ Draw border lines
  205.     LineApi 0, 3, 0, lh - 3, .cBorders(eSTATE)
  206.     LineApi 3, lh - 1, lw - 3, lh - 1, .cBorders(eSTATE)
  207.     LineApi lw - 1, 3, lw - 1, lh - 3, .cBorders(eSTATE)
  208.     If eSTATE = eDISABLE Or eSTATE = eDOWN Then '----------------------- Fill the back color (DISABLE, DOWN)
  209.         SetRect r, 1, 1, lw - 1, lh - 1
  210.         If eSTATE = eDISABLE Then
  211.             Call DrawFilled(r, 15398133)
  212.         Else
  213.             Call DrawFilled(r, 14607335)
  214.         End If
  215.     Else
  216.         SetRect r, 1, 3, lw - 1, lh - 2 '------------------------------- Draw side gradients
  217.         Call DrawGradient(r, .cSideGradTop(eSTATE), .cSideGradBottom(eSTATE))
  218.         SetRect r, 3, 3, lw - 3, lh - 3 '------------------------------- Draw background gradient (IDLE, HOT, FOCUS)
  219.         Call DrawGradient(r, 16514300, 15133676)
  220.         LineApi 1, 1, lw, 1, .cTopLine1(eSTATE) '----------------------- Draw fade at the top
  221.         LineApi 1, 2, lw, 2, .cTopLine2(eSTATE)
  222.         LineApi 1, lh - 3, lw, lh - 3, .cBottomLine1(eSTATE) '---------- Draw fade at the bottom
  223.         LineApi 2, lh - 2, lw - 1, lh - 2, .cBottomLine2(eSTATE)
  224.     End If
  225.     SetPixel lHdc, 0, 1, .cCornerPixel2(eSTATE) '----------------------- Top left Corner
  226.     SetPixel lHdc, 0, 2, .cCornerPixel1(eSTATE)
  227.     SetPixel lHdc, 1, 0, .cCornerPixel2(eSTATE)
  228.     SetPixel lHdc, 1, 1, .cCornerPixel3(eSTATE)
  229.     SetPixel lHdc, 2, 0, .cCornerPixel1(eSTATE)
  230.     SetPixel lHdc, (lw - 1), 1, .cCornerPixel2(eSTATE) '---------------- Top right corner
  231.     SetPixel lHdc, lw - 1, 2, .cCornerPixel1(eSTATE)
  232.     SetPixel lHdc, lw - 2, 0, .cCornerPixel2(eSTATE)
  233.     SetPixel lHdc, lw - 2, 1, .cCornerPixel3(eSTATE)
  234.     SetPixel lHdc, lw - 3, 0, .cCornerPixel1(eSTATE)
  235.     SetPixel lHdc, 0, lh - 2, .cCornerPixel2(eSTATE) '------------------ Bottom left corner
  236.     SetPixel lHdc, 0, lh - 3, .cCornerPixel1(eSTATE)
  237.     SetPixel lHdc, 1, lh - 1, .cCornerPixel2(eSTATE)
  238.     SetPixel lHdc, 1, lh - 2, .cCornerPixel3(eSTATE)
  239.     SetPixel lHdc, 2, lh - 1, .cCornerPixel1(eSTATE)
  240.     SetPixel lHdc, lw - 1, lh - 2, .cCornerPixel2(eSTATE) '------------- Bottom right corner
  241.     SetPixel lHdc, lw - 1, lh - 3, .cCornerPixel1(eSTATE)
  242.     SetPixel lHdc, lw - 2, lh - 1, .cCornerPixel2(eSTATE)
  243.     SetPixel lHdc, lw - 2, lh - 2, .cCornerPixel3(eSTATE)
  244.     SetPixel lHdc, lw - 3, lh - 1, .cCornerPixel1(eSTATE)
  245.     hRgn = CreateRoundRectRgn(0, 0, lw + 1, lh + 1, 3, 3) '------------- Clip extreme corner pixels
  246.     Call SetWindowRgn(UserControl.hWnd, hRgn, True)
  247.     DeleteObject hRgn
  248. End With
  249. bSkipDrawing = True '--------------------------------------------------- Draw caption
  250. SetRect r, 3, 3, lw - 3, lh - 3
  251. UserControl.ForeColor = IIf(pENABLED, pFORECOLOR, 9609633)
  252. Call DrawText(lHdc, pCAPTION, -1, r, DT_FLAGS + DT_CENTER)
  253. If bHasFocus And pFOCUSRECT And (eSTATE > 1) Then '--------------------- Draw focus rect
  254.     UserControl.ForeColor = 0
  255.     Call DrawFocusRect(lHdc, r)
  256. End If
  257. bSkipDrawing = False
  258.  
  259. End Sub
  260.  
  261. Private Sub DrawGradient(r As RECT, ByVal StartColor As Long, ByVal EndColor As Long)
  262. Dim s       As RGBColor '--- Start RGB colors
  263. Dim e       As RGBColor '--- End RBG colors
  264. Dim i       As RGBColor '--- Increment RGB colors
  265. Dim x       As Long
  266. Dim lSteps  As Long
  267. Dim lHdc    As Long
  268.     lHdc = UserControl.hdc
  269.     lSteps = r.Bottom - r.Top
  270.     s.r = (StartColor And &HFF)
  271.     s.G = (StartColor \ &H100) And &HFF
  272.     s.B = (StartColor And &HFF0000) / &H10000
  273.     e.r = (EndColor And &HFF)
  274.     e.G = (EndColor \ &H100) And &HFF
  275.     e.B = (EndColor And &HFF0000) / &H10000
  276.     With i
  277.         .r = (s.r - e.r) / lSteps
  278.         .G = (s.G - e.G) / lSteps
  279.         .B = (s.B - e.B) / lSteps
  280.         For x = 0 To lSteps
  281.             Call LineApi(r.Left, (lSteps - x) + r.Top, r.Right, (lSteps - x) + r.Top, RGB(e.r + (x * .r), e.G + (x * .G), e.B + (x * .B)))
  282.         Next x
  283.     End With
  284. End Sub
  285.  
  286. Private Sub DrawFilled(tR As RECT, ByVal cBackColor As Long)
  287. Dim hBrush As Long
  288.     hBrush = CreateSolidBrush(cBackColor) '----------------- Fill with solid brush
  289.     FillRect UserControl.hdc, tR, hBrush
  290.     DeleteObject hBrush
  291. End Sub
  292.  
  293. Private Sub LineApi(ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal Color As Long)
  294. Dim pt      As POINT
  295. Dim hPen    As Long
  296. Dim hPenOld As Long
  297. Dim lHdc    As Long
  298.     lHdc = UserControl.hdc
  299.     hPen = CreatePen(0, 1, Color)
  300.     hPenOld = SelectObject(lHdc, hPen)
  301.     MoveToEx lHdc, X1, Y1, pt
  302.     LineTo lHdc, X2, Y2
  303.     SelectObject lHdc, hPenOld
  304.     DeleteObject hPen
  305. End Sub
  306.  
  307. Private Sub FillColorScheme()
  308.     With tColors
  309.         .cBorders(0) = 12240841 '--------- Store Disabled Colors
  310.         .cTopLine1(0) = 15726583
  311.         .cTopLine2(0) = 15726583
  312.         .cCornerPixel1(0) = 9220548
  313.         .cCornerPixel2(0) = 12437454
  314.         .cCornerPixel3(0) = 9220548
  315.         .cBorders(1) = 7617536 '---------- Store Idle Colors
  316.         .cTopLine1(1) = 16777215
  317.         .cTopLine2(1) = 16711422
  318.         .cBottomLine1(1) = 14082018
  319.         .cBottomLine2(1) = 12964054
  320.         .cCornerPixel1(1) = 8672545
  321.         .cCornerPixel2(1) = 11376251
  322.         .cCornerPixel3(1) = 10845522
  323.         .cSideGradTop(1) = 16514300
  324.         .cSideGradBottom(1) = 15133676
  325.         .cBorders(2) = 7617536 '---------- Store Focus Colors
  326.         .cTopLine1(2) = 16771022
  327.         .cTopLine2(2) = 16242621
  328.         .cBottomLine1(2) = 15183500
  329.         .cBottomLine2(2) = 15696491
  330.         .cCornerPixel1(2) = 8672545
  331.         .cCornerPixel2(2) = 11376251
  332.         .cCornerPixel3(2) = 10845522
  333.         .cSideGradTop(2) = 16241597
  334.         .cSideGradBottom(2) = 15183500
  335.         .cBorders(3) = 7617536 '---------- Store Hot Colors
  336.         .cTopLine1(3) = 13562879
  337.         .cTopLine2(3) = 9231359
  338.         .cBottomLine1(3) = 3257087
  339.         .cBottomLine2(3) = 38630
  340.         .cCornerPixel1(3) = 8672545
  341.         .cCornerPixel2(3) = 11376251
  342.         .cCornerPixel3(3) = 10845522
  343.         .cSideGradTop(3) = 10280929
  344.         .cSideGradBottom(3) = 3192575
  345.         .cBorders(4) = 7617536 '---------- Store Down Colors.
  346.         .cTopLine1(4) = 14607335
  347.         .cTopLine2(4) = 14607335
  348.         .cBottomLine1(4) = 13289407
  349.         .cCornerPixel1(4) = 8672545
  350.         .cCornerPixel2(4) = 11376251
  351.         .cCornerPixel3(4) = 10845522
  352.     End With
  353. End Sub
  354.  
  355. Private Function GetAccessKey() As String
  356. '//Extracts and returns the AccessKey appropriate for passed caption
  357. '..Function provided by LiTe Templer (Guenter Wirth)
  358. Dim lPos    As Long
  359. Dim lLen    As Long
  360. Dim lSearch As Long
  361. Dim sChr    As String
  362.     lLen = Len(pCAPTION)
  363.     If lLen = 0 Then Exit Function
  364.     lPos = 1
  365.     Do While lPos + 1 < lLen
  366.         lSearch = InStr(lPos, pCAPTION, "&")
  367.         If lSearch = 0 Or lSearch = lLen Then Exit Do
  368.         sChr = LCase$(Mid$(pCAPTION, lSearch + 1, 1))
  369.         If sChr = "&" Then
  370.             lPos = lSearch + 2
  371.         Else
  372.             GetAccessKey = sChr
  373.             Exit Do
  374.         End If
  375.     Loop
  376. End Function
  377.  
  378. Private Sub TrackMouseLeave(ByVal lng_hWnd As Long)
  379. On Error GoTo Errs
  380. Dim tme As TRACKMOUSEEVENT_STRUCT
  381.     With tme
  382.         .cbSize = Len(tme)
  383.         .dwFlags = TME_LEAVE
  384.         .hwndTrack = lng_hWnd
  385.     End With
  386.     Call TrackMouseEvent(tme) '---- Track the mouse leaving the indicated window via subclassing
  387. Errs:
  388. End Sub
  389.  
  390. 'Subclass handler - MUST be the first Public routine in this file. That includes public properties also
  391. Public Sub zSubclass_Proc(ByVal bBefore As Boolean, ByRef bHandled As Boolean, ByRef lReturn As Long, ByRef lng_hWnd As Long, ByRef uMsg As Long, ByRef wParam As Long, ByRef lParam As Long)
  392.     Select Case uMsg
  393.         Case WM_MOUSELEAVE
  394.             bMouseInControl = False
  395.             If bSpaceBarIsDown Then Exit Sub
  396.             If eSTATE <> eDISABLE Then
  397.                 If bHasFocus Or bDisplayAsDefault Then
  398.                     If eSTATE = eDOWN Then
  399.                         If bButtonIsDown Then
  400.                             Call DrawButton(eFOCUS)
  401.                         Else
  402.                             If eSTATE <> eIDLE Then Call DrawButton(eIDLE)
  403.                         End If
  404.                     Else
  405.                         If eSTATE <> eFOCUS Then
  406.                             If bParentActive Then Call DrawButton(eFOCUS)
  407.                         End If
  408.                     End If
  409.                 Else
  410.                     If eSTATE <> eIDLE Then Call DrawButton(eIDLE)
  411.                 End If
  412.             End If
  413.             
  414.         Case WM_NCACTIVATE, WM_ACTIVATE
  415.             If wParam Then  '----------------------------------- Activated
  416.                 bParentActive = True
  417.                 If pENABLED Then
  418.                     If bMouseInControl Then
  419.                         If eSTATE <> eHOT Then Call DrawButton(eHOT)
  420.                     Else
  421.                         If (bHasFocus Or bDisplayAsDefault) Then Call DrawButton(eFOCUS)
  422.                     End If
  423.                 End If
  424.                 RaiseEvent FormActivate(Active)
  425.             Else            '----------------------------------- Deactivated
  426.                 bParentActive = False
  427.                 bButtonIsDown = False
  428.                 bMouseButtonIsDown = False
  429.                 bSpaceBarIsDown = False
  430.                 If pENABLED Then If eSTATE <> eIDLE Then Call DrawButton(eIDLE)
  431.                 RaiseEvent FormActivate(InActive)
  432.             End If
  433.     End Select
  434. End Sub
  435.  
  436. Public Sub SnapMouseTo()
  437. On Error Resume Next
  438. Dim pt As POINT
  439.     With UserControl
  440.         '//Get screen coordinates of button
  441.         Call ClientToScreen(.hWnd, pt)
  442.         '//Move mouse to center of button
  443.         Call SetCursorPos(pt.x + .ScaleX(.ScaleWidth / 2, .ScaleMode, vbPixels), _
  444.             pt.y + .ScaleY(.ScaleHeight / 2, .ScaleMode, vbPixels))
  445.     End With
  446. End Sub
  447.  
  448. Private Sub UserControl_AccessKeyPress(KeyAscii As Integer)
  449.     If pENABLED Then
  450.         If bSpaceBarIsDown Then
  451.             bSpaceBarIsDown = False
  452.             bButtonIsDown = False
  453.             If bMouseInControl Then
  454.                 If eSTATE <> eHOT Then Call DrawButton(eHOT)
  455.             Else
  456.                 Call DrawButton(eFOCUS)
  457.             End If
  458.         Else
  459.             DoEvents '------------------ Process "GotFocus" before Click event
  460.             RaiseEvent Click
  461.         End If
  462.     End If
  463. End Sub
  464.  
  465. Private Sub UserControl_AmbientChanged(PropertyName As String)
  466.     bDisplayAsDefault = Ambient.DisplayAsDefault
  467.     If Not pENABLED Or bMouseInControl Then Exit Sub
  468.     If PropertyName = "DisplayAsDefault" Then
  469.         If bDisplayAsDefault Then
  470.             Call DrawButton(eFOCUS)
  471.         Else
  472.             If eSTATE <> eIDLE Then Call DrawButton(eIDLE)
  473.         End If
  474.     End If
  475. End Sub
  476.  
  477. Private Sub UserControl_Initialize()
  478.     bSkipDrawing = 1
  479.     Call FillColorScheme '-------------- Assign color variables for all states
  480.     Set pFONT = UserControl.Font
  481.     pHWND = UserControl.hWnd
  482. End Sub
  483.  
  484. Private Sub UserControl_InitProperties()
  485. Dim s   As String
  486. Dim c   As Control
  487.     s = "|" '---------------------------- Try to assume new buttons caption
  488.     For Each c In Parent.Controls       ' This saves me time on most forms :-)
  489.         If TypeOf c Is ccXPButton Then s = s & c.Caption & "|"
  490.     Next c
  491.     If InStr(1, s, "|&OK|") = 0 Then
  492.         Caption = "&OK"
  493.     ElseIf InStr(1, s, "|&Cancel|") = 0 Then
  494.         Caption = "&Cancel"
  495.     ElseIf InStr(1, s, "|&Apply|") = 0 Then
  496.         Caption = "&Apply"
  497.     Else
  498.         Caption = Extender.name
  499.     End If
  500.     ForeColor = &H0
  501.     Enabled = True
  502.     FocusRect = True
  503. End Sub
  504.  
  505. Private Sub UserControl_KeyUp(KeyCode As Integer, Shift As Integer)
  506.     If KeyCode = 32 And Not bMouseButtonIsDown Then '---------- Spacebar
  507.         If bMouseInControl Then
  508.             If eSTATE <> eHOT Then Call DrawButton(eHOT)
  509.         Else
  510.             Call DrawButton(eFOCUS)
  511.         End If
  512.         If bButtonIsDown Then RaiseEvent Click
  513.         bSpaceBarIsDown = False
  514.         bButtonIsDown = False
  515.     End If
  516. End Sub
  517.  
  518. Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
  519.     With UserControl
  520.         If x > .ScaleWidth Or x < 0 Or y > .ScaleHeight Or y < 0 Then
  521.             bMouseInControl = False
  522.         Else
  523.             bMouseInControl = True
  524.             Call TrackMouseLeave(pHWND)
  525.         End If
  526.     End With
  527.     If Not bParentActive Or bSpaceBarIsDown Then Exit Sub
  528.     If bMouseInControl Then
  529.         If bButtonIsDown Then
  530.             If eSTATE <> eDOWN Then Call DrawButton(eDOWN)
  531.         Else
  532.             If eSTATE <> eHOT Then Call DrawButton(eHOT)
  533.         End If
  534.     Else
  535.         If bHasFocus Then
  536.             If eSTATE <> eFOCUS Then Call DrawButton(eFOCUS)
  537.         Else
  538.             If eSTATE <> eIDLE Then Call DrawButton(eIDLE)
  539.         End If
  540.     End If
  541.     RaiseEvent MouseMove(Button, Shift, x, y)
  542. End Sub
  543.  
  544. Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
  545.     iDownButton = Button '-------- Remember button pressed for DblClick event
  546.     If Button = 1 Then
  547.         bHasFocus = True
  548.         bButtonIsDown = True
  549.         bMouseButtonIsDown = True
  550.         If eSTATE <> eDOWN Then DrawButton (eDOWN)
  551.     End If
  552. End Sub
  553.  
  554. Private Sub UserControl_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
  555.     If Button = 1 Then
  556.         If bParentActive Then
  557.             If bMouseInControl Then
  558.                 If eSTATE <> eHOT Then Call DrawButton(eHOT)
  559.             Else
  560.                 If bHasFocus Then Call DrawButton(eFOCUS)
  561.             End If
  562.             If bMouseInControl And bHasFocus And bButtonIsDown Then RaiseEvent Click
  563.         End If
  564.         bButtonIsDown = False
  565.         bMouseButtonIsDown = False
  566.     End If
  567. End Sub
  568.  
  569. Private Sub UserControl_DblClick()
  570.     If iDownButton = 1 Then '------- Only react to left mouse button
  571.         Call DrawButton(eDOWN)
  572.         RaiseEvent DblClick
  573.     End If
  574. End Sub
  575.  
  576. Private Sub UserControl_KeyDown(KeyCode As Integer, Shift As Integer)
  577.     Select Case KeyCode
  578.         Case 13 '------------------- Enter key
  579.             RaiseEvent Click
  580.         Case 37, 38 '--------------- Left Arrow and Up keys
  581.             SendKeys "+{TAB}"
  582.         Case 39, 40 '--------------- Right Arrow and Down keys
  583.             SendKeys "{TAB}"
  584.         Case 32 '------------------- Spacebar
  585.             If Not bMouseButtonIsDown Then
  586.                 bSpaceBarIsDown = True
  587.                 bButtonIsDown = True
  588.                 If eSTATE <> eDOWN Then Call DrawButton(eDOWN)
  589.             End If
  590.     End Select
  591. End Sub
  592.  
  593. Private Sub UserControl_GotFocus()
  594.     bHasFocus = True
  595.     If bMouseInControl Then
  596.         If eSTATE <> eHOT And eSTATE <> eDOWN Then Call DrawButton(eHOT)
  597.     Else
  598.         If Not bButtonIsDown Then Call DrawButton(eFOCUS)
  599.     End If
  600. End Sub
  601.  
  602. Private Sub UserControl_LostFocus()
  603.     bHasFocus = False
  604.     bButtonIsDown = False
  605.     bSpaceBarIsDown = False
  606.     If pENABLED Then
  607.         If Not bParentActive Then
  608.             If eSTATE <> eIDLE Then Call DrawButton(eIDLE)
  609.         ElseIf bMouseInControl Then
  610.             If eSTATE <> eHOT Then Call DrawButton(eHOT)
  611.         Else
  612.             If bDisplayAsDefault Then
  613.                 Call DrawButton(eFOCUS)
  614.             Else
  615.                 If eSTATE <> eIDLE Then Call DrawButton(eIDLE)
  616.             End If
  617.         End If
  618.     End If
  619. End Sub
  620.  
  621. Private Sub UserControl_Resize()
  622.     With UserControl
  623.         If .Height < 100 Then bSkipDrawing = True: .Height = 100
  624.         If .Width < 100 Then bSkipDrawing = True: .Width = 100
  625.     End With
  626.     If Not bSkipDrawing Then Call DrawButton(eSTATE)
  627. End Sub
  628.  
  629. Private Sub UserControl_Terminate()
  630. On Error GoTo Errs
  631.     Set pFONT = Nothing
  632.     If Ambient.UserMode Then
  633.         Call Subclass_Stop(pHWND)
  634.         Call Subclass_Stop(lParentHwnd)
  635.     End If
  636. Errs:
  637. End Sub
  638.  
  639. Public Property Get hWnd() As Long
  640.     hWnd = pHWND
  641. End Property
  642.  
  643. Public Property Let Caption(ByVal NewValue As String)
  644.     pCAPTION = NewValue
  645.     UserControl.AccessKeys = GetAccessKey '---------- Set AccessKey property if desired
  646.     Call DrawButton(eSTATE)
  647.     UserControl.PropertyChanged "Caption"
  648. End Property
  649.  
  650. Public Property Get Caption() As String
  651. Attribute Caption.VB_UserMemId = -518
  652.     Caption = pCAPTION
  653. End Property
  654.  
  655. Public Property Let Enabled(ByVal NewValue As Boolean)
  656.     pENABLED = NewValue
  657.     UserControl.Enabled = pENABLED
  658.     bSkipDrawing = 0
  659.     If bMouseInControl And pENABLED Then
  660.         Call DrawButton(eHOT)
  661.     Else
  662.         If bDisplayAsDefault And NewValue Then
  663.             Call DrawButton(eFOCUS)
  664.         Else
  665.             If eSTATE <> Abs(NewValue) Then Call DrawButton(Abs(NewValue))
  666.         End If
  667.     End If
  668.     UserControl.PropertyChanged "Enabled"
  669. End Property
  670.  
  671. Public Property Get Enabled() As Boolean
  672.     Enabled = pENABLED
  673. End Property
  674.  
  675. Public Property Get Font() As StdFont
  676.     Set Font = pFONT
  677. End Property
  678.  
  679. Public Property Set Font(NewValue As StdFont)
  680.     Set pFONT = NewValue
  681.     Call pFONT_FontChanged("")
  682. End Property
  683.  
  684. Private Sub pFONT_FontChanged(ByVal PropertyName As String)
  685.     Set UserControl.Font = pFONT
  686.     Call DrawButton(eSTATE)
  687.     UserControl.PropertyChanged "Font"
  688. End Sub
  689.  
  690. Public Property Let ForeColor(NewValue As OLE_COLOR)
  691.     pFORECOLOR = NewValue
  692.     UserControl.ForeColor = pFORECOLOR
  693.     Call DrawButton(eSTATE)
  694.     UserControl.PropertyChanged "ForeColor"
  695. End Property
  696.  
  697. Public Property Get ForeColor() As OLE_COLOR
  698.     ForeColor = pFORECOLOR
  699. End Property
  700.  
  701. Public Property Let FocusRect(NewValue As Boolean)
  702. Attribute FocusRect.VB_Description = "Displays a rect inside button border when the control has the focus."
  703.     pFOCUSRECT = NewValue
  704.     If bHasFocus Then Call DrawButton(eSTATE)
  705.     UserControl.PropertyChanged "FocusRect"
  706. End Property
  707.  
  708. Public Property Get FocusRect() As Boolean
  709.     FocusRect = pFOCUSRECT
  710. End Property
  711.  
  712. Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
  713.     lParentHwnd = UserControl.Parent.hWnd
  714.     With PropBag
  715.         Caption = .ReadProperty("Caption", "&OK")
  716.         ForeColor = .ReadProperty("ForeColor", 0)
  717.         Set Font = .ReadProperty("Font", pFONT)
  718.         FocusRect = .ReadProperty("FocusRect", True)
  719.         Enabled = .ReadProperty("Enabled", True) '--- Keep as last read property for bSkipDrawing variable during initialize
  720.     End With
  721.     If Ambient.UserMode Then
  722.         Call Subclass_Start(pHWND)
  723.         Call Subclass_AddMsg(pHWND, WM_MOUSELEAVE, MSG_AFTER)
  724.         Call Subclass_Start(lParentHwnd)
  725.         If UserControl.Parent.MDIChild Then
  726.             Call Subclass_AddMsg(lParentHwnd, WM_NCACTIVATE, MSG_AFTER)
  727.         Else
  728.             Call Subclass_AddMsg(lParentHwnd, WM_ACTIVATE, MSG_AFTER)
  729.         End If
  730.     End If
  731.     bSkipDrawing = False: Call DrawButton(eSTATE)
  732. End Sub
  733.  
  734. Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
  735.     With PropBag
  736.         .WriteProperty "Caption", pCAPTION, "&OK"
  737.         .WriteProperty "Enabled", pENABLED, True
  738.         .WriteProperty "ForeColor", pFORECOLOR, 0
  739.         .WriteProperty "Font", pFONT, "Verdana"
  740.         .WriteProperty "FocusRect", pFOCUSRECT, True
  741.     End With
  742. End Sub
  743.  
  744. '========================================================================================
  745. 'Start Subclass code - The programmer may call any of the following Subclass_??? routines
  746.  
  747.  
  748. 'Add a message to the table of those that will invoke a callback. You should Subclass_Start first and then add the messages
  749. Private Sub Subclass_AddMsg(ByVal lng_hWnd As Long, ByVal uMsg As Long, Optional ByVal When As eMsgWhen = MSG_AFTER)
  750. On Error GoTo Errs
  751. 'Parameters:
  752.   'lng_hWnd  - The handle of the window for which the uMsg is to be added to the callback table
  753.   'uMsg      - The message number that will invoke a callback. NB Can also be ALL_MESSAGES, ie all messages will callback
  754.   'When      - Whether the msg is to callback before, after or both with respect to the the default (previous) handler
  755.   With sc_aSubData(zIdx(lng_hWnd))
  756.     If When And eMsgWhen.MSG_BEFORE Then
  757.       Call zAddMsg(uMsg, .aMsgTblB, .nMsgCntB, eMsgWhen.MSG_BEFORE, .nAddrSub)
  758.     End If
  759.     If When And eMsgWhen.MSG_AFTER Then
  760.       Call zAddMsg(uMsg, .aMsgTblA, .nMsgCntA, eMsgWhen.MSG_AFTER, .nAddrSub)
  761.     End If
  762.   End With
  763. Errs:
  764. End Sub
  765.  
  766.  
  767. 'Return whether we're running in the IDE.
  768. Private Function Subclass_InIDE() As Boolean
  769.   Debug.Assert zSetTrue(Subclass_InIDE)
  770. End Function
  771.  
  772. 'Start subclassing the passed window handle
  773. Private Function Subclass_Start(ByVal lng_hWnd As Long) As Long
  774. On Error GoTo Errs
  775. 'Parameters:
  776.   'lng_hWnd  - The handle of the window to be subclassed
  777. 'Returns;
  778.   'The sc_aSubData() index
  779.   Const CODE_LEN              As Long = 202                                             'Length of the machine code in bytes
  780.   Const FUNC_CWP              As String = "CallWindowProcA"                             'We use CallWindowProc to call the original WndProc
  781.   Const FUNC_EBM              As String = "EbMode"                                      'VBA's EbMode function allows the machine code thunk to know if the IDE has stopped or is on a breakpoint
  782.   Const FUNC_SWL              As String = "SetWindowLongA"                              'SetWindowLongA allows the cSubclasser machine code thunk to unsubclass the subclasser itself if it detects via the EbMode function that the IDE has stopped
  783.   Const MOD_USER              As String = "user32"                                      'Location of the SetWindowLongA & CallWindowProc functions
  784.   Const MOD_VBA5              As String = "vba5"                                        'Location of the EbMode function if running VB5
  785.   Const MOD_VBA6              As String = "vba6"                                        'Location of the EbMode function if running VB6
  786.   Const PATCH_01              As Long = 18                                              'Code buffer offset to the location of the relative address to EbMode
  787.   Const PATCH_02              As Long = 68                                              'Address of the previous WndProc
  788.   Const PATCH_03              As Long = 78                                              'Relative address of SetWindowsLong
  789.   Const PATCH_06              As Long = 116                                             'Address of the previous WndProc
  790.   Const PATCH_07              As Long = 121                                             'Relative address of CallWindowProc
  791.   Const PATCH_0A              As Long = 186                                             'Address of the owner object
  792.   Static aBuf(1 To CODE_LEN)  As Byte                                                   'Static code buffer byte array
  793.   Static pCWP                 As Long                                                   'Address of the CallWindowsProc
  794.   Static pEbMode              As Long                                                   'Address of the EbMode IDE break/stop/running function
  795.   Static pSWL                 As Long                                                   'Address of the SetWindowsLong function
  796.   Dim i                       As Long                                                   'Loop index
  797.   Dim j                       As Long                                                   'Loop index
  798.   Dim nSubIdx                 As Long                                                   'Subclass data index
  799.   Dim sHex                    As String                                                 'Hex code string
  800.   
  801. 'If it's the first time through here..
  802.   If aBuf(1) = 0 Then
  803.   
  804. 'The hex pair machine code representation.
  805.     sHex = "5589E583C4F85731C08945FC8945F8EB0EE80000000083F802742185C07424E830000000837DF800750AE838000000E84D00" & _
  806.            "00005F8B45FCC9C21000E826000000EBF168000000006AFCFF7508E800000000EBE031D24ABF00000000B900000000E82D00" & _
  807.            "0000C3FF7514FF7510FF750CFF75086800000000E8000000008945FCC331D2BF00000000B900000000E801000000C3E33209" & _
  808.            "C978078B450CF2AF75278D4514508D4510508D450C508D4508508D45FC508D45F85052B800000000508B00FF90A4070000C3"
  809.  
  810. 'Convert the string from hex pairs to bytes and store in the static machine code buffer
  811.     i = 1
  812.     Do While j < CODE_LEN
  813.       j = j + 1
  814.       aBuf(j) = Val("&H" & Mid$(sHex, i, 2))                                            'Convert a pair of hex characters to an eight-bit value and store in the static code buffer array
  815.       i = i + 2
  816.     Loop                                                                                'Next pair of hex characters
  817.     
  818. 'Get API function addresses
  819.     If Subclass_InIDE Then                                                              'If we're running in the VB IDE
  820.       aBuf(16) = &H90                                                                   'Patch the code buffer to enable the IDE state code
  821.       aBuf(17) = &H90                                                                   'Patch the code buffer to enable the IDE state code
  822.       pEbMode = zAddrFunc(MOD_VBA6, FUNC_EBM)                                           'Get the address of EbMode in vba6.dll
  823.       If pEbMode = 0 Then                                                               'Found?
  824.         pEbMode = zAddrFunc(MOD_VBA5, FUNC_EBM)                                         'VB5 perhaps
  825.       End If
  826.     End If
  827.     
  828.     pCWP = zAddrFunc(MOD_USER, FUNC_CWP)                                                'Get the address of the CallWindowsProc function
  829.     pSWL = zAddrFunc(MOD_USER, FUNC_SWL)                                                'Get the address of the SetWindowLongA function
  830.     ReDim sc_aSubData(0 To 0) As tSubData                                               'Create the first sc_aSubData element
  831.   Else
  832.     nSubIdx = zIdx(lng_hWnd, True)
  833.     If nSubIdx = -1 Then                                                                'If an sc_aSubData element isn't being re-cycled
  834.       nSubIdx = UBound(sc_aSubData()) + 1                                               'Calculate the next element
  835.       ReDim Preserve sc_aSubData(0 To nSubIdx) As tSubData                              'Create a new sc_aSubData element
  836.     End If
  837.     
  838.     Subclass_Start = nSubIdx
  839.   End If
  840.  
  841.   With sc_aSubData(nSubIdx)
  842.     .hWnd = lng_hWnd                                                                    'Store the hWnd
  843.     .nAddrSub = GlobalAlloc(GMEM_FIXED, CODE_LEN)                                       'Allocate memory for the machine code WndProc
  844.     .nAddrOrig = SetWindowLongA(.hWnd, GWL_WNDPROC, .nAddrSub)                          'Set our WndProc in place
  845.     Call RtlMoveMemory(ByVal .nAddrSub, aBuf(1), CODE_LEN)                              'Copy the machine code from the static byte array to the code array in sc_aSubData
  846.     Call zPatchRel(.nAddrSub, PATCH_01, pEbMode)                                        'Patch the relative address to the VBA EbMode api function, whether we need to not.. hardly worth testing
  847.     Call zPatchVal(.nAddrSub, PATCH_02, .nAddrOrig)                                     'Original WndProc address for CallWindowProc, call the original WndProc
  848.     Call zPatchRel(.nAddrSub, PATCH_03, pSWL)                                           'Patch the relative address of the SetWindowLongA api function
  849.     Call zPatchVal(.nAddrSub, PATCH_06, .nAddrOrig)                                     'Original WndProc address for SetWindowLongA, unsubclass on IDE stop
  850.     Call zPatchRel(.nAddrSub, PATCH_07, pCWP)                                           'Patch the relative address of the CallWindowProc api function
  851.     Call zPatchVal(.nAddrSub, PATCH_0A, ObjPtr(Me))                                     'Patch the address of this object instance into the static machine code buffer
  852.   End With
  853. Errs:
  854. End Function
  855.  
  856. 'Stop subclassing the passed window handle
  857. Private Sub Subclass_Stop(ByVal lng_hWnd As Long)
  858. On Error GoTo Errs
  859. 'Parameters:
  860.   'lng_hWnd  - The handle of the window to stop being subclassed
  861.   With sc_aSubData(zIdx(lng_hWnd))
  862.     Call SetWindowLongA(.hWnd, GWL_WNDPROC, .nAddrOrig)                                 'Restore the original WndProc
  863.     Call zPatchVal(.nAddrSub, PATCH_05, 0)                                              'Patch the Table B entry count to ensure no further 'before' callbacks
  864.     Call zPatchVal(.nAddrSub, PATCH_09, 0)                                              'Patch the Table A entry count to ensure no further 'after' callbacks
  865.     Call GlobalFree(.nAddrSub)                                                          'Release the machine code memory
  866.     .hWnd = 0                                                                           'Mark the sc_aSubData element as available for re-use
  867.     .nMsgCntB = 0                                                                       'Clear the before table
  868.     .nMsgCntA = 0                                                                       'Clear the after table
  869.     Erase .aMsgTblB                                                                     'Erase the before table
  870.     Erase .aMsgTblA                                                                     'Erase the after table
  871.   End With
  872. Errs:
  873. End Sub
  874.  
  875. '=======================================================================================================
  876. 'These z??? routines are exclusively called by the Subclass_??? routines.
  877.  
  878. 'Worker sub for Subclass_AddMsg
  879. Private Sub zAddMsg(ByVal uMsg As Long, ByRef aMsgTbl() As Long, ByRef nMsgCnt As Long, ByVal When As eMsgWhen, ByVal nAddr As Long)
  880. On Error GoTo Errs
  881.   Dim nEntry  As Long                                                                   'Message table entry index
  882.   Dim nOff1   As Long                                                                   'Machine code buffer offset 1
  883.   Dim nOff2   As Long                                                                   'Machine code buffer offset 2
  884.   
  885.   If uMsg = ALL_MESSAGES Then                                                           'If all messages
  886.     nMsgCnt = ALL_MESSAGES                                                              'Indicates that all messages will callback
  887.   Else                                                                                  'Else a specific message number
  888.     Do While nEntry < nMsgCnt                                                           'For each existing entry. NB will skip if nMsgCnt = 0
  889.       nEntry = nEntry + 1
  890.       
  891.       If aMsgTbl(nEntry) = 0 Then                                                       'This msg table slot is a deleted entry
  892.         aMsgTbl(nEntry) = uMsg                                                          'Re-use this entry
  893.         Exit Sub                                                                        'Bail
  894.       ElseIf aMsgTbl(nEntry) = uMsg Then                                                'The msg is already in the table!
  895.         Exit Sub                                                                        'Bail
  896.       End If
  897.     Loop                                                                                'Next entry
  898.  
  899.     nMsgCnt = nMsgCnt + 1                                                               'New slot required, bump the table entry count
  900.     ReDim Preserve aMsgTbl(1 To nMsgCnt) As Long                                        'Bump the size of the table.
  901.     aMsgTbl(nMsgCnt) = uMsg                                                             'Store the message number in the table
  902.   End If
  903.  
  904.   If When = eMsgWhen.MSG_BEFORE Then                                                    'If before
  905.     nOff1 = PATCH_04                                                                    'Offset to the Before table
  906.     nOff2 = PATCH_05                                                                    'Offset to the Before table entry count
  907.   Else                                                                                  'Else after
  908.     nOff1 = PATCH_08                                                                    'Offset to the After table
  909.     nOff2 = PATCH_09                                                                    'Offset to the After table entry count
  910.   End If
  911.  
  912.   If uMsg <> ALL_MESSAGES Then
  913.     Call zPatchVal(nAddr, nOff1, VarPtr(aMsgTbl(1)))                                    'Address of the msg table, has to be re-patched because Redim Preserve will move it in memory.
  914.   End If
  915.   Call zPatchVal(nAddr, nOff2, nMsgCnt)                                                 'Patch the appropriate table entry count
  916. Errs:
  917. End Sub
  918.  
  919. 'Return the memory address of the passed function in the passed dll
  920. Private Function zAddrFunc(ByVal sDLL As String, ByVal sProc As String) As Long
  921.   zAddrFunc = GetProcAddress(GetModuleHandleA(sDLL), sProc)
  922.   Debug.Assert zAddrFunc                                                                'You may wish to comment out this line if you're using vb5 else the EbMode GetProcAddress will stop here everytime because we look for vba6.dll first
  923. End Function
  924.  
  925. 'Get the sc_aSubData() array index of the passed hWnd
  926. Private Function zIdx(ByVal lng_hWnd As Long, Optional ByVal bAdd As Boolean = False) As Long
  927. On Error GoTo Errs
  928. 'Get the upper bound of sc_aSubData() - If you get an error here, you're probably Subclass_AddMsg-ing before Subclass_Start
  929.   zIdx = UBound(sc_aSubData)
  930.   Do While zIdx >= 0                                                                    'Iterate through the existing sc_aSubData() elements
  931.     With sc_aSubData(zIdx)
  932.       If .hWnd = lng_hWnd Then                                                          'If the hWnd of this element is the one we're looking for
  933.         If Not bAdd Then                                                                'If we're searching not adding
  934.           Exit Function                                                                 'Found
  935.         End If
  936.       ElseIf .hWnd = 0 Then                                                             'If this an element marked for reuse.
  937.         If bAdd Then                                                                    'If we're adding
  938.           Exit Function                                                                 'Re-use it
  939.         End If
  940.       End If
  941.     End With
  942.     zIdx = zIdx - 1                                                                     'Decrement the index
  943.   Loop
  944.   
  945. '  If Not bAdd Then
  946. '    Debug.Assert False                                                                  'hWnd not found, programmer error
  947. '  End If
  948. Errs:
  949.  
  950. 'If we exit here, we're returning -1, no freed elements were found
  951. End Function
  952.  
  953. 'Patch the machine code buffer at the indicated offset with the relative address to the target address.
  954. Private Sub zPatchRel(ByVal nAddr As Long, ByVal nOffset As Long, ByVal nTargetAddr As Long)
  955.   Call RtlMoveMemory(ByVal nAddr + nOffset, nTargetAddr - nAddr - nOffset - 4, 4)
  956. End Sub
  957.  
  958. 'Patch the machine code buffer at the indicated offset with the passed value
  959. Private Sub zPatchVal(ByVal nAddr As Long, ByVal nOffset As Long, ByVal nValue As Long)
  960.   Call RtlMoveMemory(ByVal nAddr + nOffset, nValue, 4)
  961. End Sub
  962.  
  963. 'Worker function for Subclass_InIDE
  964. Private Function zSetTrue(ByRef bValue As Boolean) As Boolean
  965.   zSetTrue = True
  966.   bValue = True
  967. End Function
  968.  
  969. 'END Subclassing Code===================================================================================
  970.