Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Mouse hooks in component dll - error on close

Status
Not open for further replies.

Borvik

Programmer
Jan 2, 2002
1,392
US
Sorry for the long post - but I want to make sure the situation is well understood.

I'm building myself a custom column for a DataGridView that has to keep track of the mouse state within the cell so basically a derivative of the ButtonColumn.

The column (cell more accurately) I built didn't perform as I had wished so I'm going more low level, and trying to first duplicate the functionality of the DataGridViewButtonColumn, and then modify the code I come up with.

If we look at how the real column works we can see that the cell knows if it wasn't the original cell clicked on (click button, move mouse to another cell in the same column and back).

I figured I can use a shared variable to keep track of which cell in the column was clicked on - but being stored in a point variable it wasn't enough (Point.Empty is 0,0 and that cell exists) so I had to implement a boolean to keep track of if it was set or not.
Code:
Private Shared mouseDownCell As Point = Point.Empty
Private Shared mouseDownCellSet As Boolean = False
It was easy enough to set that to True in the OnMouseDown sub:
Code:
Protected Overrides Sub OnMouseDown(ByVal e As DataGridViewCellMouseEventArgs)
        If ((Not MyBase.DataGridView Is Nothing) AndAlso ((e.Button = MouseButtons.Left) AndAlso DataGridViewImageButtonCell.mouseInContentBounds)) Then
            DataGridViewImageButtonCell.mouseDownCell = New Point(e.ColumnIndex, e.RowIndex)
            DataGridViewImageButtonCell.mouseDownCellSet = True
        End If
    End Sub
However it's not so easy unsetting it in MouseUp because the mouse could go up in another cell (not my code) or the form, or even Windows.

This would have to be implemented using low-level mouse hooks. I have this setup using the following:
Code:
Private Shared Sub globalMouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
    mouseDownCellSet = False
End Sub
Private Shared colMouseHooks As New MouseHook(AddressOf globalMouseUp)
In the constructor of MouseHook it calls SetWindowsHookEx and initializes it's own handler, and raises it's own event.
Code:
Public Event MouseUp As MouseEventHandler
Public Sub New(ByVal mouseUpHandler As MouseEventHandler)
    InstallHooks()

    AddHandler Me.MouseUp, mouseUpHandler
End Sub
The problem now rests with closing the application (or maybe even the form - I've only tested with a single form app).

Upon disposing (and Finalizing) of the MouseHook class it attempts to unhook the handler using UnhookWindowsHookEx. This however is returning an error:
Code:
Dim releaseResult As Boolean = True
releaseResult = Win32.UnhookWindowsHookEx(hMouseHook)
If Not releaseResult Then
    Dim errCode As Integer = Marshal.GetLastWin32Error()
    Throw New Win32Exception(errCode)
End If
The error being thrown has the error code of 2 which turns out to be "The system cannot find the file specified".

The hook is most definitely being assigned successfully and being stored in hMouseHook. It appears as though something is being released before it has a chance to call the Unhook routine.

I have tried calling GC.KeepAlive on the delegate function being used in the SetWindowsHookEx call (stored in a local variable of the MouseHook class). I have tried placing that call in several different locations and it still doesn't work.

Any ideas on how to properly release this hook or even how to do it without the hook?

Thanks.
 
I think I've found a solution.

Instead of the GC.KeepAlive I've added a AddHandler call.

Upon a successfully hooking the mouse events, I add a handler to Application.ApplicationExit. As the variable instantiating the MouseHook class is shared - this only happens once for the entire application (both the addhandler and the handled event.

When the application exits then, the event is triggered (again only once) and calls the RemoveHook routine. It successfully completes the removal without errors. The RemoveHook routine does get run again (on the dispose/finalize call), but as the hooks have already been removed - it doesn't attempt to remove them again, and therefore doesn't generate an exception.

I hope this can help someone else out - Cheers!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top