Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. Visual Basic
  4. Multi threading and garbage collected problem using RegisterShellHook and SetWindowLong API.

Multi threading and garbage collected problem using RegisterShellHook and SetWindowLong API.

Scheduled Pinned Locked Moved Visual Basic
helplinuxjson
6 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    slam Iqbal
    wrote on last edited by
    #1

    Multi threading and garbage collected problem using RegisterShellHook and SetWindowLong API.
    The purpose of this to notify when a Removable Drive is Inserted AND Removed.
    Most of the time it works properly but when I shake the form it causes an error. This is the code of my class:

    Public Class Shell_Hook_Pointer
    Public Shadows Delegate Function WndProc(ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

    Private Declare Function RegisterShellHook Lib "Shell32" Alias "#181" (ByVal hwnd As Integer, ByVal nAction As Integer) As Integer
    Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
    Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As WndProc) As Integer
    
    Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer) As Integer
    
    Private Const GWL\_WNDPROC As Short = (-4)
    Private Const RSH\_DEREGISTER As Short = 0
    
    Public Enum SheelHookTypes
        RSH\_REGISTER = 1
        RSH\_REGISTER\_PROGMAN = 2
        RSH\_REGISTER\_TASKMAN = 3
    End Enum
    Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Integer, ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
    Private OldProc As Integer
    Private uRegMsg As Integer
    Private hWnd As Integer, ParentWindowHadler As Form
    Public Const wMsg\_REMOVABLE\_DEVICE = 537
    Public Const wParam\_REMOVABLE\_DEVICE\_INSERTED = 32768
    Public Const wParam\_REMOVABLE\_DEVICE\_REMOVED = 32772
    ''' <summary>
    ''' Envent argumetns
    ''' </summary>
    ''' <remarks></remarks>
    Class ShellHookEventArgs
        Inherits System.EventArgs
        Public Enum REMOVABLE\_DEVICE
            NONE = 0
            INSERTED = 1
            REMOVED = 2
        End Enum
        Public hWnd, wMsg, wParam, lParam As Integer
        Public REMOVABLE\_DEVICE\_INFO As REMOVABLE\_DEVICE
        Public TempMsg As String
        Public SheelHookTypes = SheelHookTypes
    End Class
    
    Public Function WndProcedure(ByVal hWnd As Integer, ByVal wMsg As Integer, \_
                                 ByVal wParam As Integer, ByVal lParam As Integer) As Integer
    
    L 1 Reply Last reply
    0
    • S slam Iqbal

      Multi threading and garbage collected problem using RegisterShellHook and SetWindowLong API.
      The purpose of this to notify when a Removable Drive is Inserted AND Removed.
      Most of the time it works properly but when I shake the form it causes an error. This is the code of my class:

      Public Class Shell_Hook_Pointer
      Public Shadows Delegate Function WndProc(ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

      Private Declare Function RegisterShellHook Lib "Shell32" Alias "#181" (ByVal hwnd As Integer, ByVal nAction As Integer) As Integer
      Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
      Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As WndProc) As Integer
      
      Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer) As Integer
      
      Private Const GWL\_WNDPROC As Short = (-4)
      Private Const RSH\_DEREGISTER As Short = 0
      
      Public Enum SheelHookTypes
          RSH\_REGISTER = 1
          RSH\_REGISTER\_PROGMAN = 2
          RSH\_REGISTER\_TASKMAN = 3
      End Enum
      Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Integer, ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
      Private OldProc As Integer
      Private uRegMsg As Integer
      Private hWnd As Integer, ParentWindowHadler As Form
      Public Const wMsg\_REMOVABLE\_DEVICE = 537
      Public Const wParam\_REMOVABLE\_DEVICE\_INSERTED = 32768
      Public Const wParam\_REMOVABLE\_DEVICE\_REMOVED = 32772
      ''' <summary>
      ''' Envent argumetns
      ''' </summary>
      ''' <remarks></remarks>
      Class ShellHookEventArgs
          Inherits System.EventArgs
          Public Enum REMOVABLE\_DEVICE
              NONE = 0
              INSERTED = 1
              REMOVED = 2
          End Enum
          Public hWnd, wMsg, wParam, lParam As Integer
          Public REMOVABLE\_DEVICE\_INFO As REMOVABLE\_DEVICE
          Public TempMsg As String
          Public SheelHookTypes = SheelHookTypes
      End Class
      
      Public Function WndProcedure(ByVal hWnd As Integer, ByVal wMsg As Integer, \_
                                   ByVal wParam As Integer, ByVal lParam As Integer) As Integer
      
      L Offline
      L Offline
      Luc Pattyn
      wrote on last edited by
      #2

      Hi, I don't think multi-threading is involved; however shaking your Form will create new objects, possibly causing the GC to run and remove a delegate that is still in use as you have stored it in unmanaged memory (SetWindowLong) so the CG cannot work out it is still in use. This is not the right way to handle this. I do have similar functionality implemented in C#, without using any shell hooks: Windows will always broadcast Windows messages whenever a device gets added or removed, there is no need to subscribe to anything; all it takes is overriding WndProc and checking for such messages. This is the heart of the matter (the constant values are 0x0219, 0x8000, 0x8004):

      protected override void WndProc(ref Message m) {
      if (m.Msg==LPWIN_Constants.WM_DEVICECHANGE) {
      int wParam=(int)m.WParam;
      int lParam=(int)m.LParam;
      if (xpanel!=null) {
      if (wParam==LPWIN_Constants.DBT_DEVICEARRIVAL) xpanel.DeviceAdded();
      else if (wParam==LPWIN_Constants.DBT_DEVICEREMOVECOMPLETE) xpanel.DeviceRemoved();
      }
      }
      }

      :)

      Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

      Please use < PRE > tags for code snippets, it preserves indentation, and improves readability.

      S 1 Reply Last reply
      0
      • L Luc Pattyn

        Hi, I don't think multi-threading is involved; however shaking your Form will create new objects, possibly causing the GC to run and remove a delegate that is still in use as you have stored it in unmanaged memory (SetWindowLong) so the CG cannot work out it is still in use. This is not the right way to handle this. I do have similar functionality implemented in C#, without using any shell hooks: Windows will always broadcast Windows messages whenever a device gets added or removed, there is no need to subscribe to anything; all it takes is overriding WndProc and checking for such messages. This is the heart of the matter (the constant values are 0x0219, 0x8000, 0x8004):

        protected override void WndProc(ref Message m) {
        if (m.Msg==LPWIN_Constants.WM_DEVICECHANGE) {
        int wParam=(int)m.WParam;
        int lParam=(int)m.LParam;
        if (xpanel!=null) {
        if (wParam==LPWIN_Constants.DBT_DEVICEARRIVAL) xpanel.DeviceAdded();
        else if (wParam==LPWIN_Constants.DBT_DEVICEREMOVECOMPLETE) xpanel.DeviceRemoved();
        }
        }
        }

        :)

        Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

        Please use < PRE > tags for code snippets, it preserves indentation, and improves readability.

        S Offline
        S Offline
        slam Iqbal
        wrote on last edited by
        #3

        "I do have similar functionality implemented in C#, without using any shell hooks"
        Yeah you are right. I used shell hooks to monitor processes or forms.
        Here is my simplyfied code:

        Public Class HOOK_RMV_DEVICE
        Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Integer, _
        ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

        Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
        Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As WndProc) As Integer
        
        Private Shadows Delegate Function WndProc(ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
        
        Private Const GWL\_WNDPROC = -4
        Private IsHooked As Boolean, lpPrevWndProc As Integer, WindowHandler As Integer
        '
        Private Const wMsg\_REMOVABLE\_DEVICE = 537
        Private Const wParam\_REMOVABLE\_DEVICE\_INSERTED = 32768
        Private Const wParam\_REMOVABLE\_DEVICE\_REMOVED = 32772
        
        Public Function Hook() As Boolean
            If IsHooked Then
                Return False
            Else
                lpPrevWndProc = SetWindowLong(WindowHandler, GWL\_WNDPROC, AddressOf WindowProc)
                IsHooked = True
                Return True
            End If
        End Function
        Public Sub Unhook()
            Dim temp As Long
            temp = SetWindowLong(WindowHandler, GWL\_WNDPROC, lpPrevWndProc)
            IsHooked = False
        End Sub
        
        Function WindowProc(ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
            Dim e As New ShellHookEventArgs
            e.hWnd = hWnd
            e.wMsg = wMsg
            e.wParam = wParam
            e.lParam = lParam
            e.REMOVABLE\_DEVICE\_INFO = ShellHookEventArgs.REMOVABLE\_DEVICE.NONE
        
            If wMsg = wMsg\_REMOVABLE\_DEVICE Then
                If wParam = wParam\_REMOVABLE\_DEVICE\_INSERTED Then
                    e.REMOVABLE\_DEVICE\_INFO = ShellHookEventArgs.REMOVABLE\_DEVICE.INSERTED
                    RaiseEvent Removable\_Device(Me, e)
                ElseIf wParam = wParam\_REMOVABLE\_DEVICE\_REMOVED Then
                    e.REMOVABLE\_DEVICE\_INFO = ShellHookEventArgs.REMOVABLE\_DEVICE.REMOVED
                    RaiseEvent Removable\_Device(Me, e)
                End If
            Else
                '
        
        L 1 Reply Last reply
        0
        • S slam Iqbal

          "I do have similar functionality implemented in C#, without using any shell hooks"
          Yeah you are right. I used shell hooks to monitor processes or forms.
          Here is my simplyfied code:

          Public Class HOOK_RMV_DEVICE
          Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Integer, _
          ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

          Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
          Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As WndProc) As Integer
          
          Private Shadows Delegate Function WndProc(ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
          
          Private Const GWL\_WNDPROC = -4
          Private IsHooked As Boolean, lpPrevWndProc As Integer, WindowHandler As Integer
          '
          Private Const wMsg\_REMOVABLE\_DEVICE = 537
          Private Const wParam\_REMOVABLE\_DEVICE\_INSERTED = 32768
          Private Const wParam\_REMOVABLE\_DEVICE\_REMOVED = 32772
          
          Public Function Hook() As Boolean
              If IsHooked Then
                  Return False
              Else
                  lpPrevWndProc = SetWindowLong(WindowHandler, GWL\_WNDPROC, AddressOf WindowProc)
                  IsHooked = True
                  Return True
              End If
          End Function
          Public Sub Unhook()
              Dim temp As Long
              temp = SetWindowLong(WindowHandler, GWL\_WNDPROC, lpPrevWndProc)
              IsHooked = False
          End Sub
          
          Function WindowProc(ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
              Dim e As New ShellHookEventArgs
              e.hWnd = hWnd
              e.wMsg = wMsg
              e.wParam = wParam
              e.lParam = lParam
              e.REMOVABLE\_DEVICE\_INFO = ShellHookEventArgs.REMOVABLE\_DEVICE.NONE
          
              If wMsg = wMsg\_REMOVABLE\_DEVICE Then
                  If wParam = wParam\_REMOVABLE\_DEVICE\_INSERTED Then
                      e.REMOVABLE\_DEVICE\_INFO = ShellHookEventArgs.REMOVABLE\_DEVICE.INSERTED
                      RaiseEvent Removable\_Device(Me, e)
                  ElseIf wParam = wParam\_REMOVABLE\_DEVICE\_REMOVED Then
                      e.REMOVABLE\_DEVICE\_INFO = ShellHookEventArgs.REMOVABLE\_DEVICE.REMOVED
                      RaiseEvent Removable\_Device(Me, e)
                  End If
              Else
                  '
          
          L Offline
          L Offline
          Luc Pattyn
          wrote on last edited by
          #4

          The code I gave earlier is all there is to it. You should remove your P/Invoke stuff, the SetWindowLong calls, etc. However, you are missing the Overrides keyword that makes your windowproc replace the default one for your Form. Look at the documentation for Overrides, and search some examples. I don't have any VB code around right now. :)

          Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

          Please use < PRE > tags for code snippets, it preserves indentation, and improves readability.

          S 1 Reply Last reply
          0
          • L Luc Pattyn

            The code I gave earlier is all there is to it. You should remove your P/Invoke stuff, the SetWindowLong calls, etc. However, you are missing the Overrides keyword that makes your windowproc replace the default one for your Form. Look at the documentation for Overrides, and search some examples. I don't have any VB code around right now. :)

            Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

            Please use < PRE > tags for code snippets, it preserves indentation, and improves readability.

            S Offline
            S Offline
            slam Iqbal
            wrote on last edited by
            #5

            Thanks I got it :-D
            I forget add this line:

            MyBase.WndProc(m)

            It gives all my solution.

            L 1 Reply Last reply
            0
            • S slam Iqbal

              Thanks I got it :-D
              I forget add this line:

              MyBase.WndProc(m)

              It gives all my solution.

              L Offline
              L Offline
              Luc Pattyn
              wrote on last edited by
              #6

              you're welcome. :)

              Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

              Please use < PRE > tags for code snippets, it preserves indentation, and improves readability.

              1 Reply Last reply
              0
              Reply
              • Reply as topic
              Log in to reply
              • Oldest to Newest
              • Newest to Oldest
              • Most Votes


              • Login

              • Don't have an account? Register

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • World
              • Users
              • Groups