Python天天美味(36) - 用Python实现Spy++

本文介绍如何使用Python和PyWin32库模仿微软的Spy++工具,实现一个简化版的PySpy++工具,用于捕获和显示Windows窗口信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spy++是微软出品的用来获取Window窗口信息的一个小工具。实现的原理其实不难,通过调用某些特定的Windows API即可。于是,我打算用Python也实现一个功能简化版本的小工具,取名叫PySpy++。Python中调用Windows API一般使用pywin32这套库,界面库我使用PyQT4。


Spy++原理

Spy++中,最常用的一个功能,就是识别窗口。其中主要需要用到的Windows API有:

获取当前鼠标位置

BOOL GetCursorPos( LPPOINT lpPoint );

获取位于指定位置的窗口句柄

HWND WindowFromPoint( POINT Point );

获取窗口类别

int  GetClassName( HWND hWnd, LPTSTR lpClassName,  int  nMaxCount );

获取窗口内容或标题

方法一:

int  GetWindowText( HWND hWnd, LPTSTR lpString,  int  nMaxCount );

这个API有时候不能取到某些控件的值,因此,使用方法二。

方法二:

给窗口发送WM_GETTEXT消息:

LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM  wParam, LPARAM lParam );

高亮选中的窗口

先获取当前窗口的大小,然后画一个矩形框。

复制代码
BOOL GetWindowRect( HWND hWnd, LPRECT lpRect );

BOOL Rectangle(
    HDC hdc, 
//  handle to DC
     int  nLeftRect,  //  x-coord of upper-left corner of rectangle
     int  nTopRect,  //  y-coord of upper-left corner of rectangle
     int  nRightRect,  //  x-coord of lower-right corner of rectangle
     int  nBottomRect  //  y-coord of lower-right corner of rectangle
);
复制代码

鼠标移开窗口后,窗口需要恢复原状,需要重新刷新:

复制代码
BOOL InvalidateRect(
    HWND hWnd, 
//  handle to window
    CONST RECT *  lpRect,  //  rectangle coordinates
    BOOL bErase  //  erase state
);

BOOL UpdateWindow(
    HWND hWnd 
//  handle to window
);

BOOL RedrawWindow(
    HWND hWnd, 
//  handle to window
    CONST RECT  * lprcUpdate,  //  update rectangle
    HRGN hrgnUpdate,  //  handle to update region
    UINT flags  //  array of redraw flags
);
复制代码


PyWin32对应的函数

在Python中调用Windows API,首先下载PyWin32,地址:http://pywin32.sourceforge.net/

安装完成后,打开帮助文档Python for Windows Documentation,里面有所有需要的东西,随时用来查看。

常用的API在win32api模块里,界面相关的API在win32gui模块里,API参数中定义的一些常量在win32con模块中。上面的Windows API对应PyWin32中的函数为:

复制代码
(int, int)  =  win32gui. GetCursorPos ()
int 
=  win32gui. WindowFromPoint (point)
string 
=  win32gui. GetClassName (hwnd)
string 
=  win32gui. GetWindowText (hwnd)
int 
=  win32gui. SendMessage (hwnd, message , wparam , lparam )
(left, top, right, bottom) 
=  win32gui. GetWindowRect (hwnd)
win32gui.
Rectangle (hdc, LeftRect, TopRect, RightRect, BottomRect)
win32gui.
InvalidateRect (hWnd, Rect, Erase)
win32gui.
UpdateWindow (hwnd)
win32gui.
RedrawWindow (hWnd, rcUpdate, hrgnUpdate, flags)
复制代码


代码实现

界面库使用PyQT4,参考资料可以从我之前的一篇博客里了解:PyQt4 学习资料汇总

工具对话框窗口有两个控件,一个是QLabel控件,一个是QTextEdit控件。QLabel控件就是那个用来鼠标按下去后去捕捉窗口,QTextEdit控件用来显示窗口的信息。为了让QTextEdit响应自定义的鼠标事件,我创建了一个自定义QLabel控件SpyLabel,继承自QLabel。

复制代码
class  SpyLabel(QtGui.QLabel):
    
def   __init__ (self, parent  =  None):
        QtGui.QLabel.
__init__ (self, parent)
        self.parent 
=  parent
        self.spying 
=  False
        self.rectanglePen 
=  win32gui.CreatePen(win32con.PS_SOLID,  3 , win32api.RGB( 255 , 0, 0))
        self.prevWindow 
=  None
        self.setCursor(QtCore.Qt.SizeAllCursor)
复制代码

SpyLabel中处理鼠标移动事件:

复制代码
def  mouseMoveEvent(self, event):
    
if  self.spying:
        curX, curY 
=  win32gui.GetCursorPos()
        hwnd 
=  win32gui. WindowFromPoint ((curX, curY))

        
if  self.checkWindowValidity(hwnd):               
            
if  self.prevWindow:
                self.refreshWindow(self.prevWindow)
            self.prevWindow 
=  hwnd
            self.highlightWindow(hwnd)
            self.displayWindowInformation(hwnd)
复制代码

鼠标松开事件:

复制代码
def  mouseReleaseEvent(self, event):
    
if  self.spying:
        
if  self.prevWindow:
            self.refreshWindow(self.prevWindow)
        win32gui.ReleaseCapture()
        self.spying 
=  False
复制代码

高亮窗口的函数:

复制代码
def  highlightWindow(self, hwnd):
    left, top, right, bottom 
=  win32gui.GetWindowRect(hwnd)
    windowDc 
=  win32gui.GetWindowDC(hwnd)
    
if  windowDc:
        prevPen 
=  win32gui.SelectObject(windowDc, self.rectanglePen)
        prevBrush 
=  win32gui.SelectObject(windowDc, win32gui.GetStockObject(win32con.HOLLOW_BRUSH))

        win32gui.
Rectangle (windowDc, 0, 0, right  -  left, bottom  -  top)
        win32gui.SelectObject(windowDc, prevPen)
        win32gui.SelectObject(windowDc, prevBrush)
        win32gui.ReleaseDC(hwnd, windowDc)
复制代码

刷新窗口的函数:

复制代码
def  refreshWindow(self, hwnd):
    win32gui.
InvalidateRect (hwnd, None, True)
    win32gui.
UpdateWindow (hwnd)
    win32gui.
RedrawWindow (hwnd, 
        None, 
        None,  
        win32con.RDW_FRAME
|
            win32con.RDW_INVALIDATE
|
            win32con.RDW_UPDATENOW
|
            win32con.RDW_ALLCHILDREN)
复制代码

显示窗口信息:

复制代码
def  displayWindowInformation(self, hwnd):
    className 
=  win32gui.GetClassName(hwnd)
    buf_size 
=   1   +  win32gui. SendMessage (hwnd, win32con.WM_GETTEXTLENGTH, 0, 0)
    buffer 
=  win32gui.PyMakeBuffer(buf_size)
    win32gui.
SendMessage (hwnd, win32con.WM_GETTEXT, buf_size, buffer)
    windowText 
=  buffer[:buf_size]

    
try :
        windowText 
=  unicode(windowText,  ' gbk ' )
    
except :
        
pass

    message 
=  [ ' Handle:\t '   +  str(hwnd),
               
' Class Name:\t '   +  className,
               
' Window Text:\t '   +  windowText]
    self.output(
' \r\n ' .join(message))
复制代码

注意到上面SendMessage函数,需要传入一个分配的缓冲区,用于获取返回的内容。这里使用了:

buffer  =  win32gui.PyMakeBuffer(buf_size)

由于返回的内容中可能有中文,因此使用unicode(windowText, 'gbk')进行一下转换。


演示

image

image

 

二进制下载:

http://pyspyplusplus.googlecode.com/files/pyspy++.exe 

源代码:

http://code.google.com/p/pyspyplusplus/ 

 

 

Python 天天美味系列(总)
Python 天天美味(31) - python数据结构与算法之插入排序 

Python 天天美味(32) - python数据结构与算法之堆排序 

Python 天天美味(33) - 五分钟理解元类(Metaclasses)[转]

Python 天天美味(34) - Decorators详解

Python 天天美味(35) - 细品lambda 

 

 

本文转自CoderZh博客园博客,原文链接:http://www.cnblogs.com/coderzh/archive/2010/05/02/python-cookbook-pyspy.html,如需转载请自行联系原作者

### PythonSpy++的集成 对于希望深入了解Python应用程序行为并进行调试或者性能分析的开发者来说,Spy++主要应用于Windows平台上的C/C++开发环境Visual Studio中用于监视窗口消息、进程和线程等。然而,在提到Python时,并不存在直接名为“Python Spy++”的技术或工具。 如果目标是在Python环境中实现类似于Spy++的功能,则可以考虑其他替代方案: #### 使用`py-spy` 一种可能的选择是使用专门设计来分析Python应用性能的第三方库如`py-spy`[^1]。此工具允许用户无需修改任何代码即可跟踪正在运行的应用程序,并提供CPU采样概况数据以及阻塞调用堆栈信息。它通过命令行界面操作,支持多种可视化方式展示结果,非常适合用来诊断慢查询或其他效率低下之处。 ```bash # 安装 py-spy pip install py-spy # 启动待监测的目标Python脚本 python my_program.py # 开始实时监控指定PID对应的Python进程 py-spy top --pid <process_id> ``` 需要注意的是,虽然`py-spy`提供了强大的功能集,但它并不具备像传统意义上的Spy++那样全面的消息捕获能力;而是专注于性能剖析方面的工作。 #### Windows API 和 `ctypes/winsdk` 为了更接近于原生Spy++所提供的特性,即监听特定事件(比如鼠标点击、键盘输入),可以通过Python访问底层操作系统API接口达成目的。这通常涉及到使用`ctypes`模块加载Win32 DLL函数或将官方提供的SDK绑定到项目里[^3]。例如,利用这些技术能够创建自定义GUI自动化测试框架或是构建辅助性质的小型桌面实用程序。 ```python import ctypes user32 = ctypes.windll.User32 hwnd = user32.FindWindowW(None, 'Target Window Title') if hwnd != 0: print('Found window:', hwnd) else: print('Could not find the target window.') ``` 上述例子展示了怎样借助`FindWindowW()`去定位某个已知标题栏文字的活动窗口句柄。进一步地,还可以探索更多有关发送模拟按键指令(`SendInput`)、枚举子控件(`EnumChildWindows`)等功能点。 综上所述,“Python Spy++”并非实际存在的产品名称,但对于追求相似效果的人来说,确实存在若干可行路径可供尝试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值