|
用API函数,就会提到句柄,像SendMessage, GetWindowText等,最常用到的参数就是句柄。啥是句柄呢?就是窗口的锅把儿,你拎着它,整个锅儿都听你的话。那啥是窗口呢?不仅仅指我们常说的窗体Form,还包括所有控件,如文本框,按钮,复选框等等。这些句柄怎么获取呢?用Spy++呀。本文中,笔者就和您一起打造一个VB版的Spy++。(笔者以前写过一篇VC版的Spy++,得到了许多网友的关注,所以撰写此文,以飨VB战壕的朋友们。)
一. 界面设计
新建EXE工程,在窗体frmMain上画一个PictureBox(picShot),用以装载探测器的靶形图标;画两个CheckBox(chkAlwaysOnTop和chkHex)用于选择窗口是否在最上面和句柄等值的返回形式是否为十六进制。
再下面是选项卡控件,我们有两种选择。一种是用Microsoft Windows Common Controls6.0(mscomctl.ocx)中的TabStrip,这种控件的用法与VC中类似,标签页不可以在主窗体设计时直接做,而是要画N个子窗体或PictureBox,然后在运行时按情况调用;第二种选择是Microsoft Tabbed Dialog Control6.0(tabctl32.ocx),这个控件可以直接在上面设计每个标签页,我们就选择它。
把选项卡控件命名为SSTab1,通过其属性页设置好各标签页的标题为“常规”、“样式”、“类”、“窗口”和“消息”。程序界面设计如图。
二、探测器制作
准备好两个图标(ico)和一个鼠标指针(cur)文件,分别用于正常状态下的显示、鼠标拖出时的显示以及拖出时的鼠标指针;正常状态下显示一幅有靶的图标。当鼠标在上面按下时,显示内容立刻换为另一幅无靶的图标,同时鼠标指针变为靶状。这样,就给人一种靶心被拖出去的感觉了。通过上面的叙述,我们了解到图片框需要响应MouseDown和MouseUp事件。而鼠标左键松开处一般不会在图片框上,所以,要想让图片框的MouseUp来响应,我们要用到一个API函数:SetCapture。这个函数用以对鼠标事件的捕获,我们在图片框的MouseDown事件中SetCapture后,以后即使鼠标指针离开图片框,发生的事件也是由图片框(或指定窗口)来处理。其API函数声明自己用API浏览器查,不再赘述。需强调的是,在MouseUp事件中别忘了释放捕获(另一个API:ReleaseCapture)。在鼠标按下时,还涉及到换图标换指针等操作,代码如下:
Private Sub picShot_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Screen.MousePointer = vbCustom
Screen.MouseIcon = Image1.Picture '用鼠标指针变为靶状
picShot.Picture = Image2.Picture '此时图片框加载另一无靶图标
'将以后的鼠标输入消息都发送到图片框窗口
SetCapture (picShot.hWnd)
End Sub
Private Sub picShot_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Screen.MousePointer = vbDefault
picShot.Picture = Image3.Picture
ReleaseCapture
End Sub
获取窗口句柄。根据鼠标位置来确定窗口需要用到API函数GetCursorPos和WindowFromPoint。此外,我们还想做到像抓图程序那样,鼠标移动到的地方,窗口四周会出现闪烁的矩形。这一点,我们用定时器来实现。在桌面背景上绘图,大致过程如下:用GetDesktopWindow()获取桌面句柄,再用GetWindowDC()获得设备场景(或叫设备上下文)。啥叫设备场景?就当成是画布吧,要画画嘛,就要找找到窗口对应的画布。还有用到一个SetROP2(),用以“设定当前前景色的混合模式”(从MSDN中直译,如果觉得不通,请看MSDN原文),这里采用R2_NOTXORPEN混合模式(其值为10),载入一支反色画笔。GetWindowRect()用以获得窗口的矩形,Rectangle()函数用于画这个矩形,ReleaseDC用于释放设备场景。实现代码如下:
Private Sub tmr_Timer()
……Dim XXX……..
DeskHwnd& = GetDesktopWindow() '取得桌面句柄
DeskDC& = GetWindowDC(DeskHwnd&) '取得桌面设备场景
oldRop2& = SetROP2(DeskDC&, 10)
GetCursorPos pnt '取得鼠标坐标
PointText.Text = Str(pnt.X) & "," & Str(pnt.Y)
UnHwnd = WindowFromPoint(pnt.X, pnt.Y) '取得鼠标指针处窗口句柄
grayHwnd = GetWindow(UnHwnd, GW_CHILD)
'因为WindowsFromPoint函数对Disabled的窗口无效,所以写了下面这个循环,否则无法选中“灰色按钮”类窗口
Do While (grayHwnd)
GetWindowRect grayHwnd, tempRc
If PtInRect(tempRc, pnt.X, pnt.Y) Then
FindIt = True
Exit Do
Else
grayHwnd = GetWindow(grayHwnd, GW_HWNDNEXT)
End If
Loop
If FindIt = True Then
FindIt = False
SnapHwnd = grayHwnd
Else
SnapHwnd = UnHwnd
End If
GetWindowRect SnapHwnd, rc '获得窗口矩形
If rc.Left < 0 Then rc.Left = 0
If rc.Top < 0 Then rc.Top = 0
If rc.Right > Screen.Width / 15 Then rc.Right = Screen.Width / 15
If rc.Bottom > Screen.Height / 15 Then rc.Bottom = Screen.Height / 15
newPen& = CreatePen(0, 3, &H0) '建立新画笔,载入DeskDC
oldPen& = SelectObject(DeskDC, newPen)
Rectangle DeskDC, rc.Left, rc.Top, rc.Right, rc.Bottom '在指示窗口周围显示闪烁矩形
Sleep tmr.Interval '设置闪烁时间间隔
Rectangle DeskDC, rc.Left, rc.Top, rc.Right, rc.Bottom
SetROP2 DeskDC, oldRop2
SelectObject DeskDC, oldPen
DeleteObject newPen
ReleaseDC DeskHwnd, DeskDC:
DeskDC = 0
End Sub
三、两个复选框
复选框“总在最上面”用到的API函数是SetWindowPos()。由于比较直观,读者朋友直接看下面代码:
Private Sub chkalwaysontop_Click()
If(chkAlwaysOnTop.Value= 1)
SetWindowPos(g_hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE)
Else
SetWindowPos(g_hWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE)
End If
End Sub
第二个复选框决定返回值以16进制还是10进制显示,这对咱VB兄弟很有用,因为我们VB习惯使用的不是16进制。这里我们建立一个全局函数,根据复选框的不同状态有不同的返回值,代码如下:
Function DisplayNum(Num As Long) As String
If frmMain.chkHex.Value = 1 Then
DisplayNum = Hex(Num)
Else
DisplayNum = LTrim(Str(Num))
End If
End Function |