VB控制外部程序
|
admin
2014年4月9日 2:31
本文热度 7749
|
最近要写一个程序,用来控制外部一个软件的设置等等,网上收集了许多资料,自己经过尝试得出了想要的效果,这里把我这两天学到的和理解的贴出来,让以后的要做类似东西的朋友少一些弯路。
首先,说一下我想实现的效果
【目标】 1、运行我的程序,可以通过我的程序打开和关闭该外部软件。
2、可以使用我的程序控制外部软件的按键点击、可以控制文本框的文本写入和读取。
(其实看起来很简单 ,还是搞了我两天时间啊…… )
其次,说一下我的方案和过程
【方法】
方法一:使用按键精灵模拟鼠标的点击事件,然后通过VB的sendkeys()函数发送按键精灵的快捷键实现点击
1、启动软件:这个有两种方法,大家都应该知道,函数有shell,该函数专门用来打开外部软件的;另外一个是API函数WinExeC。这两个都能达到启动的效果,所以启动是很简单的。
'例程Shell returnvalue = Shell("C:\Program Files\DekTec\StreamXpress\StreamXpress.exe", 1)'返回该软件的任务ID AppActivate returnvalue'激活该软件 '例程WinExeC Declare Function WinExec Lib "kernel32" Alias "WinExec" (ByVal lpCmdLine As String, ByVal nCmdShow As Long) As Long lngtemp = WinExec("C:\Program Files\DekTec\StreamXpress\StreamXpress.exe", 2)'返回值>32表示打开成功 2、移动窗体:因为只是使用按键精灵模拟鼠标点击,所以必须固定窗体的位置,否则没有效果,或者点击到其他位置,固定窗体的位置我用的是API函数MoveWindow(当然还有其他函数),将被控软件窗体移动到左上角。在需要移动窗体的过程中必须获取窗体的句柄,这里使用API函数FindWindow来获取窗体的句柄。例程如下:(前面这些可是一帆风顺啊,哈哈)
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function MoveWindow Lib "user32" (ByVal hwnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long
Public Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
hWnd = FindWindow(vbNullString, "DekTec StreamXpress - Transport-Stream Player")'获取窗体句柄
If hWnd = 0 Then
MsgBox "窗体DekTec StreamXpress Transport-Stream Player未找到"
Exit Sub
End If
GetWindowRect hWnd, RECT'获取窗体的尺寸,方便移动窗体
temp = MoveWindow(hWnd, 0, 0, RECT.Right - RECT.Left, RECT.Bottom - RECT.Top, 1) If temp = 0 Then MsgBox "Move failed!" 3、制作按键精灵鼠标点击:就是写一个单击按键的脚本,设置其快捷键方式:Alt+F10(这个要不常用,不让每次都响应按键精灵去了),上图:
就这个,够简单吧…… ,在键盘上试了试,OK,没问题……然而悲剧才刚刚开始
插一脚:这里说一个简单的获取要点击位置的方法,我看了一些很多的方法,觉得这个是最简便的,调用API的GetCursorPos函数,用鼠标指向需要点击的地方就直接获取了这一点的位置。代码如下:
Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Public Type POINTAPI
x As Long
y As Long
End Type Private Sub Timer1_Timer()‘注意要用到timer控件哈 Dim cursorp As POINTAPI GetCursorPos cursorp Text1 = cursorp.x & "," & cursorp.y End Sub 4、接下来就是使用我的程序发送快捷键了,这个简单,使用sendkeys函数即可,以前使用过,代码如下SendKeys "+{F10}"
这里出问题了…… 不管怎么搞,按键精灵就是一点响应也没有,去问了一些人,说是按键精灵设置的原因,按键精灵有普通模式、硬件模式和超级模式,一般选择超级模式就搞定了,我就找到修改了然后还是不行,最后我把快捷键设置成了一个字母就OK了,但是这会影响到其他输入……一头雾水,到现在都没弄明白。但是也因为这个原因促使我想其他方法,现在想想要用按键精灵,以后还要确保它被开启才能使用程序,也怪麻烦的,直接一个程序不加其他东西最好
方法二:使用FindWindowEx和SendMessage配合完成外部控件的控制+SPY++工具
- 上面的方法没行通,只有换方法了,我们已经从上面的方法中获取了主窗体的句柄,要控制里面的控件,就需要获取控件的句柄,通过函数 FindWindowEx就可以获取控件的句柄,于是开始动手写代码了(关于 FindWindowEx 的介绍我空间里有)
Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As
Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
htxt0 = FindWindowEx(hWnd, 0, "Edit", vbNullString) 这里 FindWindowEx需要的参数有顶级窗体句柄(已获取),子窗体句柄(就是从这个句柄后查找下一个句柄,为0表示从第一个开始),lpsz1表示类名(即控件的类名),lpsz2是一样的。
- FindWindowEx 我们一般的使用方法是如果知道标题名(如按钮“确定”),直接使用即可,如下
hbtn=FindWindowEx(hwnd,0,vbNullString,"确定")
但是我们这里按键是图形按钮,所以不能使用上面直接给出标题名的查找方法,只能通过类名进行查找,我要查找一个按钮 控件的句柄,写了如下代码
hbtn = FindWindowEx(hWnd, 0, "ThunderRT6CommandButton", vbNullString)
这个地方获取的句柄始终是0,也就是获取失败,这是什么原因呢?我查询了一些资料,发现因为我们不知到其他软件的编程工具是什么,所以就不能知道它控件的类名,这里也就是说类名是错误的。那类名如何获取呢?这里我就要隆重的推出一块强大的小工具了 Spy++,许多写过窗体的控制的朋友都知道的
这里我们可以看到这个软件能直接获取各控件的类名和句柄,这个软件的设计过程我已转到控件,可以看一下,很有帮组的。获取了句柄和类名后就可以看是写程序了 htxt0 = FindWindowEx(hWnd, 0, "Edit", vbNullString)
htxt1 = FindWindowEx(hWnd, htxt0, "Edit", vbNullString)
htxt2 = FindWindowEx(hWnd, htxt1, "Edit", vbNullString)
htxt3 = FindWindowEx(hWnd, htxt2, "Edit", vbNullString)
htxt4 = FindWindowEx(hWnd, htxt3, "Edit", vbNullString)
htxt5 = FindWindowEx(hWnd, htxt4, "Edit", vbNullString)
这是一个text控件的获取,这里我可以循环获取然后比较句柄值,同spy++获取的一样就是我们要找的控件了(目前我只知道没有标题名的控件只有这种方法,其他还没了解过),需要注意的是这个时候需要控制的软件不能关闭重新打开,因为这样句柄就变了,但是一旦找到是哪个控件,这个就不受限制了。OK控件的句柄我们已经获取到了,我们可以随意操控它了……
3.强大的SendMessage函数
API中有一个SendMessage的函数,它是一个非常强大的函数,它可以给windows系统发送许多命令,然后操控系统中的一些东西,我们这里需要发送的就是控制按键和文本的命令(具体函数的详解空间里有)
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Const WM_LBUTTONDOWN = &H201
Public Const WM_LBUTTONUP = &H202
Public Const WM_GETTEXT = &HD
Public Const WM_SETTEXT = &HC
Public Const WM_GETTEXTLENGTH = &HE
Dim bytSend(3) As Byte
bytSend(0) = &H35
bytSend(1) = &H30
bytSend(2) = &H30
SendMessage htxt, WM_SETTEXT, 0, bytSend(0)'发送字符数组到文本控件
slen = SendMessageByString(htxt, WM_GETTEXT, 255, ByVal gets)'接收文本控件里的数据,返回到gets里
注意:这里要注意传送文本到文本控件时使用的是字符数组发送,而不是字符串,字符串发送会出现非法字符。
方法三:Mouse_event的使用
mouse_event函数是一个API函数,它模拟了我们鼠标的动作,因为采用sendmessage方法我控制的按键太多需要获取太多的句柄比较麻烦,于是采用函数mouse_event,仅仅需要知道鼠标要点击的位置就可以了,而前面我说了getcursorpos函数能很好的获取坐标值,所以相比这个更好用一些,写了如下代码
Public Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Public Const MOUSEEVENTF_ABSOLUTE = &H8000 ' absolute move Public Const MOUSEEVENTF_LEFTDOWN = &H2 ' left button down Public Const MOUSEEVENTF_LEFTUP = &H4 ' left button up Public Const MOUSEEVENTF_MIDDLEDOWN = &H20 ' middle button down Public Const MOUSEEVENTF_MIDDLEUP = &H40 ' middle button up Public Const MOUSEEVENTF_MOVE = &H1 ' mouse move Public Const MOUSEEVENTF_RIGHTDOWN = &H8 ' right button down Public Const MOUSEEVENTF_RIGHTUP = &H10 ' right button up Public Const MOUSETRAILS = 39
mouse_event MOUSEEVENTF_LEFTDOWN, RECT.Left + 23, RECT.Top + 458, 0, 0
mouse_event MOUSEEVENTF_LEFTUP, RECT.Left + 23, RECT.Top + 458, 0, 0
结果鼠标是有单击动作,但是只是在原点即鼠标点单击,查了一些资料没能解决,想到它仅仅就是在鼠标指向点点击,何不将鼠标移到要点击的地方,然后通过,点击鼠标的位置达到点击效果呢,于是改变方法写了以下代码,使用 SetCursorPos函数把鼠标移动到要点击的点
Public Declare Function SetCursorPos Lib "user32" (ByVal x As Long, ByVal y As Long) As Long
SetCursorPos 23, 458
mouse_event MOUSEEVENTF_LEFTDOWN Or MOUSEEVENTF_ABSOLUTE, 0, 0, 0, 0
mouse_event MOUSEEVENTF_LEFTUP Or MOUSEEVENTF_ABSOLUTE, 0, 0, 0, 0
OK! 问题解决,当然这里的鼠标位置可以自己通过其他动态设定最好
该文章在 2014/4/9 2:31:32 编辑过
| |
全部评论1 |
|
admin
2014年4月9日 2:37
用API FindWindow查找目标窗口的句柄,再用SendMessage或PostMessage发送按键消息:
【
'Form代码:
Option Explicit
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long '
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Sub Command1_Click()
Dim l_HwndSendTo As Long '为目标窗口句柄
l_HwndSendTo = FindWindow(vbNullString, "目标窗口名") '根据窗口名获得窗口句柄
SendMessage l_HwndSendTo, &H100, 27, 0
'第二
'个参数表示发送的是按键按下消息,此时第三个参数是按键
'的Ascii码
SendMessage l_HwndSendTo, &H101, 27, 0
End Sub
】
-----------
注意可能您要发送的目标实际上可能是窗口的一个子窗口(如某个按钮),这个时候需要用FindWindowEx获得其子窗口的句柄,再用SendMessage发送消息。
{{
FindWindowEx("父窗口句柄", ByVal 0&, "子窗口类型(如Button)", "子窗口名")
}} 该评论在 2014/4/9 2:37:01 编辑过
|
|
|