handler exit routine causes api to fail

blaforce77

New member
Joined
Jul 30, 2005
Messages
3
I have been working on this for 3 days now. I dont want to use a timer but maybe that is the only way to make it work. The UnSetHotKey routine works fine using the buttons but fails when launched from the OnProcessExit routine. What am I doing wrong? Thanks, Brian.

Code:
Public Class Form1
    Inherits System.Windows.Forms.Form

    Public Declare Function RegisterHotKey Lib "user32" (ByVal hWnd As Integer, _
                                                          ByVal id As Integer, _
                                                          ByVal fsModifiers As ModConst, _
                                                          ByVal vk As Integer) As Boolean

    Public Declare Function UnregisterHotKey Lib "user32" (ByVal hWnd As Integer, _
                                                            ByVal id As Integer) As Boolean

    Dim bytUnReg As Byte

    Used as input for SetHotKey and with RegisterHotKey API call. 
    Public Enum ModConst
        MOD_ALT = &H1
        MOD_CONTROL = &H2
        MOD_CONTROLALT = &H3
        MOD_SHIFT = &H4
        MOD_ALTSHIFT = &H5
        MOD_SHIFTCONTROL = &H6
        MOD_CONTROLALTSHIFT = &H7
        MOD_WINKEY = &H8
    End Enum

    Hotkey Message. used in WndProc method 
    Private Const WM_HOTKEY = &H312

    Hot key counter. We need to keep track how may keys are regestered. 
    Private Shared HotKeyID As Integer
    Dim intPID As Integer

    Private Sub SetHotKey(ByVal Modifer As ModConst, ByVal KeyCode As Integer)
        HotKeyID = 1
        RegisterHotKey(Me.Handle.ToInt32, HotKeyID, Modifer, KeyCode)
    End Sub

    Private Sub UnSetHotKey1()

        bytUnReg = 0
        Do Until bytUnReg = 1
            UnSetHotKey(1)
        Loop
    End Sub

    Private Sub UnSetHotKey(ByVal HotKeyID As Integer)
        HotKeyID = 1

        Dim retval As Boolean = UnregisterHotKey(Me.Handle.ToInt32, HotKeyID)
        If retval = True Then
            MsgBox("Hotkeys unregistered")

        Else
            MsgBox("Hotkeys failed to unregister")
        End If

    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = WM_HOTKEY Then

            Select Case m.WParam.ToInt32
                Case 1
                    MsgBox("The First Hot Key Was Pressed")
                Case 2
                    MsgBox("The Second Hot Key Was Pressed")
                Case 3
                    MsgBox("The Third Hot Key Was Pressed")
            End Select
        Else
            MyBase.WndProc(m)
        End If
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        This is where we regester the hot keys
        SetHotKey(0, 112) This sets F1
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        UnSetHotKey(1)
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Dim proRunApp As System.Diagnostics.Process = New System.Diagnostics.Process
        proRunApp.StartInfo.FileName = "C:\Program Files\Internet Explorer\iexplore.exe"
        proRunApp.StartInfo.Arguments = ""
        proRunApp.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal

         Add an event handler to trap the Exited event. 
        proRunApp.EnableRaisingEvents() = True

        AddHandler proRunApp.Exited(), AddressOf Me.UnSetHotKey
        AddHandler proRunApp.Exited, AddressOf OnProcessExit
        proRunApp.Start()
        SetHotKey(0, 112)
    End Sub

    Friend Sub OnProcessExit(ByVal sender As Object, ByVal e As EventArgs)
         Event Handler for Process.Exited Event
         Avoid late binding and cast the Object to a specific Process component. 
        Dim proc As Process = CType(sender, Process)
        intPID = CInt(proc.Id)

         Dispose and Clear the object. 
        proc.Dispose()
        proc.Close()

        proc = Nothing
        UnSetHotKey(1)

    End Sub
 
    End Sub
End Class
 
In the description of UnregisterHotKey it says:
The UnregisterHotKey function frees a hot key previously registered by the calling thread., maybe youre using the wrong thread?

Another reason could be that the hWnd you have to pass in the unregisterhotkey API call, is no longer pointing to a valid window. If the window is already destroyed (or at least the handle invalid) when this call is done, the API call will probably fail as well. If this is the case, dont unregister the hotkey when closing the application, but when closing the form.

Try using the GetLastError API call after unregistering to see what the cause of the failure is. If GetLastError returns 6 (ERROR_INVALID_HANDLE) the hWnd is invalid.
 
Thanks for the replay, Wile. I think you hit the nail on the head about being a different thread. From what I have read, the process exit handle uses System.Diagnostics namespace which creates its own thread. So any subroutines I try to launch from within the routine are running under a different thread. Too bad MS doesnt supply a command to link back to the calling application. The handle is correct when I break so it must be a Thread problem. The process exit handler is useless for firing events after an application exits. I have an application that launches multiple applications and allows hotkey control over the applications. It would have been nice to be able to unregister the hotkeys upon exiting the apps. I guess I will have to use the WaitOnExit or Enum the active processes to unregister the hotkeys. I just hate to have a timer going slowing down the users PC.
 
Got it! Heres the code:
Code:
Public Class Form1
    Inherits System.Windows.Forms.Form

    Public Declare Function RegisterHotKey Lib "user32" (ByVal hWnd As Integer, _
                                                          ByVal id As Integer, _
                                                          ByVal fsModifiers As ModConst, _
                                                          ByVal vk As Integer) As Boolean

    Public Declare Function UnregisterHotKey Lib "user32" (ByVal hWnd As Integer, _
                                                            ByVal id As Integer) As Boolean

    Dim WithEvents p As Process

    Used as input for SetHotKey and with RegisterHotKey API call. 
    Public Enum ModConst
        MOD_ALT = &H1
        MOD_CONTROL = &H2
        MOD_CONTROLALT = &H3
        MOD_SHIFT = &H4
        MOD_ALTSHIFT = &H5
        MOD_SHIFTCONTROL = &H6
        MOD_CONTROLALTSHIFT = &H7
        MOD_WINKEY = &H8
    End Enum

    Hotkey Message. used in WndProc method 
    Private Const WM_HOTKEY = &H312

    Hot key counter. We need to keep track how may keys are regestered. 
    Private Shared HotKeyID As Integer
    Dim intPID As Integer

    Private Sub SetHotKey(ByVal Modifer As ModConst, ByVal KeyCode As Integer)
        HotKeyID = 1
        RegisterHotKey(Me.Handle.ToInt32, HotKeyID, Modifer, KeyCode)
    End Sub

    Private Sub UnSetHotKey(ByVal HotKeyID As Integer)
        HotKeyID = 1

        Dim retval As Boolean = UnregisterHotKey(Me.Handle.ToInt32, HotKeyID)
        If retval = True Then
            MsgBox("Hotkeys unregistered")
            Timer1.Enabled = False
        Else
            MsgBox("Hotkeys failed to unregister")
        End If

    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = WM_HOTKEY Then

            Select Case m.WParam.ToInt32
                Case 1
                    MsgBox("The First Hot Key Was Pressed")
                Case 2
                    MsgBox("The Second Hot Key Was Pressed")
                Case 3
                    MsgBox("The Third Hot Key Was Pressed")
            End Select
        Else
            MyBase.WndProc(m)
        End If
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    End Sub

    Private Sub p_Exited(ByVal sender As Object, ByVal e As System.EventArgs)
        MessageBox.Show("Notepad was closed")
        UnSetHotKey(1)
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        p = New Process
         Handle the Exited event that the Process class fires.
        AddHandler p.Exited, AddressOf p_Exited
        p.EnableRaisingEvents = True
        p.SynchronizingObject = Me
        p.StartInfo.FileName = "notepad.exe"
        p.Start()
        SetHotKey(0, 112)
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        UnSetHotKey(1)
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        SetHotKey(0, 112) This sets F1
    End Sub
End Class
 
Last edited by a moderator:
Back
Top