home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 1_2002.ISO / Data / Zips / CODE_UPLOAD56665102000.psc / subclass.bas < prev    next >
Encoding:
BASIC Source File  |  1998-10-15  |  11.1 KB  |  277 lines

  1. Attribute VB_Name = "MSubclass"
  2. Option Explicit
  3.  
  4. ' declares:
  5. Private Declare Function IsWindow Lib "user32" (ByVal hwnd As Long) As Long
  6. Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
  7. Private Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
  8. Private Declare Function RemoveProp Lib "user32" Alias "RemovePropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
  9. Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
  10. Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
  11. Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  12. Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
  13. Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
  14. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
  15.     lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
  16. Private Const GWL_WNDPROC = (-4)
  17.  
  18. ' SubTimer is independent of VBCore, so it hard codes error handling
  19.  
  20. Public Enum EErrorWindowProc
  21.     eeBaseWindowProc = 13080 ' WindowProc
  22.     eeCantSubclass           ' Can't subclass window
  23.     eeAlreadyAttached        ' Message already handled by another class
  24.     eeInvalidWindow          ' Invalid window
  25.     eeNoExternalWindow       ' Can't modify external window
  26. End Enum
  27.  
  28. Private m_iCurrentMessage As Long
  29. Private m_iProcOld As Long
  30.  
  31. Public Property Get CurrentMessage() As Long
  32.    CurrentMessage = m_iCurrentMessage
  33. End Property
  34.  
  35. Private Sub ErrRaise(e As Long)
  36.     Dim sText As String, sSource As String
  37.     If e > 1000 Then
  38.         sSource = App.EXEName & ".WindowProc"
  39.         Select Case e
  40.         Case eeCantSubclass
  41.             sText = "Can't subclass window"
  42.         Case eeAlreadyAttached
  43.             sText = "Message already handled by another class"
  44.         Case eeInvalidWindow
  45.             sText = "Invalid window"
  46.         Case eeNoExternalWindow
  47.             sText = "Can't modify external window"
  48.         End Select
  49.         Err.Raise e Or vbObjectError, sSource, sText
  50.     Else
  51.         ' Raise standard Visual Basic error
  52.         Err.Raise e, sSource
  53.     End If
  54. End Sub
  55.  
  56. Sub AttachMessage(iwp As ISubclass, ByVal hwnd As Long, _
  57.                   ByVal iMsg As Long)
  58.     Dim procOld As Long, f As Long, c As Long
  59.     Dim iC As Long, bFail As Boolean
  60.     
  61.     ' Validate window
  62.     If IsWindow(hwnd) = False Then ErrRaise eeInvalidWindow
  63.     If IsWindowLocal(hwnd) = False Then ErrRaise eeNoExternalWindow
  64.  
  65.     ' Get the message count
  66.     c = GetProp(hwnd, "C" & hwnd)
  67.     If c = 0 Then
  68.         ' Subclass window by installing window procecure
  69.         procOld = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WindowProc)
  70.         If procOld = 0 Then ErrRaise eeCantSubclass
  71.         ' Associate old procedure with handle
  72.         f = SetProp(hwnd, hwnd, procOld)
  73.         Debug.Assert f <> 0
  74.         ' Count this message
  75.         c = 1
  76.         f = SetProp(hwnd, "C" & hwnd, c)
  77.     Else
  78.         ' Count this message
  79.         c = c + 1
  80.         f = SetProp(hwnd, "C" & hwnd, c)
  81.     End If
  82.     Debug.Assert f <> 0
  83.     
  84.     ' SPM - in this version I am allowing more than one class to
  85.     ' make a subclass to the same hWnd and Msg.  Why am I doing
  86.     ' this?  Well say the class in question is a control, and it
  87.     ' wants to subclass its container.  In this case, we want
  88.     ' all instances of the control on the form to receive the
  89.     ' form notification message.
  90.     c = GetProp(hwnd, hwnd & "#" & iMsg & "C")
  91.     If (c > 0) Then
  92.         For iC = 1 To c
  93.             If (GetProp(hwnd, hwnd & "#" & iMsg & "#" & iC) = ObjPtr(iwp)) Then
  94.                 ErrRaise eeAlreadyAttached
  95.                 bFail = True
  96.                 Exit For
  97.             End If
  98.         Next iC
  99.     End If
  100.                 
  101.     If Not (bFail) Then
  102.         c = c + 1
  103.         ' Increase count for hWnd/Msg:
  104.         f = SetProp(hwnd, hwnd & "#" & iMsg & "C", c)
  105.         Debug.Assert f <> 0
  106.         
  107.         ' Associate object with message at the count:
  108.         f = SetProp(hwnd, hwnd & "#" & iMsg & "#" & c, ObjPtr(iwp))
  109.         Debug.Assert f <> 0
  110.     End If
  111. End Sub
  112.  
  113. Sub DetachMessage(iwp As ISubclass, ByVal hwnd As Long, _
  114.                   ByVal iMsg As Long)
  115.     Dim procOld As Long, f As Long, c As Long
  116.     Dim iC As Long, iP As Long, lPtr As Long
  117.     
  118.     ' Get the message count
  119.     c = GetProp(hwnd, "C" & hwnd)
  120.     If c = 1 Then
  121.         ' This is the last message, so unsubclass
  122.         procOld = GetProp(hwnd, hwnd)
  123.         Debug.Assert procOld <> 0
  124.         ' Unsubclass by reassigning old window procedure
  125.         Call SetWindowLong(hwnd, GWL_WNDPROC, procOld)
  126.         ' Remove unneeded handle (oldProc)
  127.         RemoveProp hwnd, hwnd
  128.         ' Remove unneeded count
  129.         RemoveProp hwnd, "C" & hwnd
  130.     Else
  131.         ' Uncount this message
  132.         c = GetProp(hwnd, "C" & hwnd)
  133.         c = c - 1
  134.         f = SetProp(hwnd, "C" & hwnd, c)
  135.     End If
  136.     
  137.     ' SPM - in this version I am allowing more than one class to
  138.     ' make a subclass to the same hWnd and Msg.  Why am I doing
  139.     ' this?  Well say the class in question is a control, and it
  140.     ' wants to subclass its container.  In this case, we want
  141.     ' all instances of the control on the form to receive the
  142.     ' form notification message.
  143.     
  144.     ' How many instances attached to this hwnd/msg?
  145.     c = GetProp(hwnd, hwnd & "#" & iMsg & "C")
  146.     If (c > 0) Then
  147.         ' Find this iwp object amongst the items:
  148.         For iC = 1 To c
  149.             If (GetProp(hwnd, hwnd & "#" & iMsg & "#" & iC) = ObjPtr(iwp)) Then
  150.                 iP = iC
  151.                 Exit For
  152.             End If
  153.         Next iC
  154.     
  155.         If (iP <> 0) Then
  156.              ' Remove this item:
  157.              For iC = iP + 1 To c
  158.                 lPtr = GetProp(hwnd, hwnd & "#" & iMsg & "#" & iC)
  159.                 SetProp hwnd, hwnd & "#" & iMsg & "#" & (iC - 1), lPtr
  160.              Next iC
  161.         End If
  162.         ' Decrement the count
  163.         RemoveProp hwnd, hwnd & "#" & iMsg & "#" & c
  164.         c = c - 1
  165.         SetProp hwnd, hwnd & "#" & iMsg & "C", c
  166.     
  167.     End If
  168. End Sub
  169.  
  170. Private Function WindowProc(ByVal hwnd As Long, ByVal iMsg As Long, _
  171.                             ByVal wParam As Long, ByVal lParam As Long) _
  172.                             As Long
  173.     Dim procOld As Long, pSubclass As Long, f As Long
  174.     Dim iwp As ISubclass, iwpT As ISubclass
  175.     Dim iPC As Long, iP As Long, bNoProcess As Long
  176.     Dim bCalled As Boolean
  177.     
  178.     ' Get the old procedure from the window
  179.     procOld = GetProp(hwnd, hwnd)
  180.     Debug.Assert procOld <> 0
  181.     
  182.     ' SPM - in this version I am allowing more than one class to
  183.     ' make a subclass to the same hWnd and Msg.  Why am I doing
  184.     ' this?  Well say the class in question is a control, and it
  185.     ' wants to subclass its container.  In this case, we want
  186.     ' all instances of the control on the form to receive the
  187.     ' form notification message.
  188.     
  189.     ' Get the number of instances for this msg/hwnd:
  190.     bCalled = False
  191.     iPC = GetProp(hwnd, hwnd & "#" & iMsg & "C")
  192.     If (iPC > 0) Then
  193.         ' For each instance attached to this msg/hwnd, call the subclass:
  194.         For iP = 1 To iPC
  195.             bNoProcess = False
  196.             ' Get the object pointer from the message
  197.             pSubclass = GetProp(hwnd, hwnd & "#" & iMsg & "#" & iP)
  198.             If pSubclass = 0 Then
  199.                 ' This message not handled, so pass on to old procedure
  200.                 WindowProc = CallWindowProc(procOld, hwnd, iMsg, _
  201.                                             wParam, ByVal lParam)
  202.                 bNoProcess = True
  203.             End If
  204.             
  205.             If Not (bNoProcess) Then
  206.                 ' Turn the pointer into an illegal, uncounted interface
  207.                 CopyMemory iwpT, pSubclass, 4
  208.                 ' Do NOT hit the End button here! You will crash!
  209.                 ' Assign to legal reference
  210.                 Set iwp = iwpT
  211.                 ' Still do NOT hit the End button here! You will still crash!
  212.                 ' Destroy the illegal reference
  213.                 CopyMemory iwpT, 0&, 4
  214.                 ' OK, hit the End button if you must--you'll probably still crash,
  215.                 ' but it will be because of the subclass, not the uncounted reference
  216.                 
  217.                 ' Store the current message, so the client can check it:
  218.                 m_iCurrentMessage = iMsg
  219.                 m_iProcOld = procOld
  220.                 
  221.                 ' Use the interface to call back to the class
  222.                 With iwp
  223.                     ' Preprocess (only check this the first time around):
  224.                     If (iP = 1) Then
  225.                         If .MsgResponse = emrPreprocess Then
  226.                            If Not (bCalled) Then
  227.                               WindowProc = CallWindowProc(procOld, hwnd, iMsg, _
  228.                                                         wParam, ByVal lParam)
  229.                               bCalled = True
  230.                            End If
  231.                         End If
  232.                     End If
  233.                     ' Consume (this message is always passed to all control
  234.                     ' instances regardless of whether any single one of them
  235.                     ' requests to consume it):
  236.                     WindowProc = .WindowProc(hwnd, iMsg, wParam, ByVal lParam)
  237.                     ' PostProcess (only check this the last time around):
  238.                     If (iP = iPC) Then
  239.                         If .MsgResponse = emrPostProcess Then
  240.                            If Not (bCalled) Then
  241.                               WindowProc = CallWindowProc(procOld, hwnd, iMsg, _
  242.                                                         wParam, ByVal lParam)
  243.                               bCalled = True
  244.                            End If
  245.                         End If
  246.                     End If
  247.                 End With
  248.             End If
  249.         Next iP
  250.     Else
  251.         ' This message not handled, so pass on to old procedure
  252.         WindowProc = CallWindowProc(procOld, hwnd, iMsg, _
  253.                                     wParam, ByVal lParam)
  254.     End If
  255. End Function
  256. Public Function CallOldWindowProc( _
  257.       ByVal hwnd As Long, _
  258.       ByVal iMsg As Long, _
  259.       ByVal wParam As Long, _
  260.       ByVal lParam As Long _
  261.    ) As Long
  262.    CallOldWindowProc = CallWindowProc(m_iProcOld, hwnd, iMsg, wParam, lParam)
  263.  
  264. End Function
  265.  
  266. ' Cheat! Cut and paste from MWinTool rather than reusing
  267. ' file because reusing file would cause many unneeded dependencies
  268. Function IsWindowLocal(ByVal hwnd As Long) As Boolean
  269.     Dim idWnd As Long
  270.     Call GetWindowThreadProcessId(hwnd, idWnd)
  271.     IsWindowLocal = (idWnd = GetCurrentProcessId())
  272. End Function
  273. '
  274.  
  275.  
  276.  
  277.