refuzee
refuzee

Reputation: 408

pyHook KeyLogger thread not finishing

I created a thread for a keylogger that logs in parallel to another thread that produces some sounds ( I want to catch reaction times).

Unfortunately, the thread never finishes although i invoke killKey() and "invoked killkey()" is printed.

I allways get an thread.isActive() = true from this thread.

class KeyHandler(threading.Thread):
    hm = pyHook.HookManager()

    def __init__(self): 
       threading.Thread.__init__(self) 

    def OnKeyboardCharEvent(self,event):
        print 'Key:', event.Key
        if event.Key=='E':
            ...   
        return True

    def killKey(self):
        KeyHandler.hm.UnhookKeyboard() 
        ctypes.windll.user32.PostQuitMessage(0) 
        print "invoked killkey()"

    def run(self):
        print "keyHandlerstartetrunning"
        KeyHandler.hm.KeyDown = self.OnKeyboardCharEvent
        KeyHandler.hm.HookKeyboard()
        #print "keyboardhooked"
        pythoncom.PumpMessages()

to be more precise, ctypes.windll.user32.PostQuitMessage(0) does nothing

I would favor an external timeout to invoke killKey(), respective ctypes.windll.user32.PostQuitMessage(0) in this thread.

Upvotes: 2

Views: 871

Answers (2)

refuzee
refuzee

Reputation: 408

I guess pbackup's solution is fine. Just to conclude I found a solution by simply sending a key myself instead of waiting for the user to input. It's proably not the best but was the fastest an goes parallel in my timing thread with the other timing routines.

    STOP_KEY_HANDLER = True

    # send key to kill handler - not pretty but works
    for hwnd in get_hwnds_for_pid (GUIWINDOW_to_send_key_to.pid):
        win32gui.PostMessage (hwnd, win32con.WM_KEYDOWN, win32con.VK_F5, 0)
    # sleep to make sure processing is done
    time.sleep(0.1)

    # kill window
    finished()

Upvotes: 1

Piotr Dabkowski
Piotr Dabkowski

Reputation: 5939

PostQuitMessage has to be posted from the same thread. To do so you need to introduce a global variable STOP_KEY_HANDLER. If you want to quit then just set global STOP_KEY_HANDLER = True from any thread you want and it will quit with the next keystroke. Your key handler has to run on the main thread.

STOP_KEY_HANDLER = False

def main():
    pass # here do all you want
    #bla bla
    global STOP_KEY_HANDLER
    STOP_KEY_HANDLER = True # This will kill KeyHandler


class KeyHandler:
    hm = pyHook.HookManager()

    def OnKeyboardCharEvent(self,event):
        if STOP_KEY_HANDLER:
            self.killKey()
        print 'Key:', event.Key
        if event.Key=='E':
            pass
        return True

    def killKey(self):
        global STOP_KEY_HANDLER
        if not STOP_KEY_HANDLER:
            STOP_KEY_HANDLER = True
            return None
        KeyHandler.hm.UnhookKeyboard()
        ctypes.windll.user32.PostQuitMessage(0)
        print "invoked killkey()"

    def _timeout(self):
        if self.timeout:
            time.sleep(self.timeout)
            self.killKey()

    def run(self, timeout=False):
        print "keyHandlerstartetrunning"
        self.timeout = timeout
        threading.Thread(target=self._timeout).start()

        KeyHandler.hm.KeyDown = self.OnKeyboardCharEvent
        KeyHandler.hm.HookKeyboard()
        #print "keyboardhooked"
        pythoncom.PumpMessages()


k=KeyHandler()

threading.Thread(target=main).start()
k.run(timeout=100) # You can specify the timeout in seconds or you can kill it directly by setting STOP_KEY_HANDLER to True.

Upvotes: 2

Related Questions