XPButton2.ctl
上传用户:luoweizhao
上传日期:2022-08-01
资源大小:1290k
文件大小:45k
源码类别:

外挂编程

开发平台:

Visual Basic

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