Multi threading and garbage collected problem using RegisterShellHook and SetWindowLong API.
-
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 IntegerPrivate 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
-
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 IntegerPrivate 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
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.
-
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.
"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 IntegerPrivate 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 '
-
"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 IntegerPrivate 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 '
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.
-
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.
Thanks I got it :-D
I forget add this line:MyBase.WndProc(m)
It gives all my solution.
-
Thanks I got it :-D
I forget add this line:MyBase.WndProc(m)
It gives all my solution.
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.