加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

用Python实现Spy++

(2013-05-02 18:56:41)
标签:

it

分类: 计算机知识

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 );

高亮选中的窗口

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

http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif
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中的函数为:

http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif
(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。

http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif
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')进行一下转换。


演示

http://images.cnblogs.com/cnblogs_com/coderzh/WindowsLiveWriter/Python36PythonSpy_B49/image_thumb_1.png

http://images.cnblogs.com/cnblogs_com/coderzh/WindowsLiveWriter/Python36PythonSpy_B49/image_thumb.png

二进制下载:

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

源代码:

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

作者:CoderZhCoderZh的技术博客 - 博客园
微博:http://t.sina.com.cn/coderzh
出处:http://coderzh.cnblogs.com
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有