Working with APIs

来源:互联网 发布:java 判断相等 编辑:程序博客网 时间:2024/05/21 12:47

微软Windows应用程序编程接口(API)为编写windows应用程序提供了许多构建好的模块。它包含了多种多样丰富的功能,如可以获取鼠标当前位置的坐标,一个窗口的句柄或它的颜色等等。在QTP中,你可以通过调用含有这些功能的DLL文件来使用它们,但是由于VBScript的局限性,有一部分APIQTP中是无法使用的。

对于本章示例所使用的API函数,读者若想进行深入学习,可以在MSDN或使用Visul Studio附带的API查看工具进行查询。

Extern对象 

QTP提供了一个名为Extern的通用对象来声明并调用API函数。 

语法:

Extern.Declare (RetType, MethodName, LibName, Alias [, ArgType(s)])

详情请参阅QTP用户手册。

VB中调用API 的语法

Private Declare Function GetForegroundWindow Lib “user32.dll” () As Long

如果想要使用这个API函数,我们需要根据它的定义,在QTP中选择合适的参数来声明:RetType = micLong(函数返回值的类型)

MethodName = “GetForegroundWindow” (可以使用任意值但最好是API函数的名称)

LibName = “user32.dll” (当不使用windows的系统DLL时,我们就必须提供一个正确的文件地址,如”C:\MyApp\Lib\mylic.dll”)

Alias = ““ or “GetForegroundWindow” (如果AliasMethodName相同,可以留白)

ArgType(s) = (这个函数不需要其它参数)

QTP API 的定义

Extern.Declare micLong,” GetForegroundWindow”,”user32.dll”,” GetForegroundWindow”

通过下面的几个示例,我们将学习如何通过API来解决一些常见的问题。

Problem 17-1. 怎样判断当前桌面最顶端的窗口是否是浏览器?

‘声明GetForegroundWindow

Extern.Declare micLong,"GetForegroundWindow","user32.dll","GetForegroundWindow"

‘获取最顶端的窗口的句柄

Hwnd = extern. GetForegroundWindow()

‘检查拥有该句柄的浏览器是否存在

isBrowser = Browser("hwnd:=" & hwnd).Exist(1)

If isBrowser then

  Msgbox "The top most window is a browser"

End if

Problem 17-2. 怎样获取Windows的环境变量(注意Windows的环境变量与QTP的环境变量是不同的)

‘声明一个变量

Dim s_EnvValue

‘声明 GetEnvironmentVaribale

Extern.Declare micLong,"GetEnvironmentVariable","kernel32.dll","GetEnvironmentVariableA", _

micString,micString+micByRef,micLong

‘取得环境变量 “TEMP” 的值

Extern.GetEnvironmentVariable "TEMP",s_EnvValue,255 ‘获的temp文件夹的路径

MsgBox s_EnvValue

Problem 17-3. 怎样通过API检查(选取)列表框中的一个选项

‘声明函数

Extern.Declare micLong,"SendMessage","user32.dll","SendMessageA",micLong,micLong,micLong,micLong

‘在列表中选择一个选项

Const LB_SETSEL = &H185

 

‘函数功能:在指定的位置开始检查列表框

Function CheckListBox(hwnd, index)

       extern.SendMessage hwnd, LB_SETSEL, True, index

End Function

‘取消选中该项

Function UnCheckListBox(hwnd, index)

       extern.SendMessage hwnd, LB_SETSEL, False, index

End Function

Problem 17-4. 怎样获取一个文本框的背景色(当必填项的背景颜色与其他项不同时就可以使用这招)

‘声明要用到的API函数

Extern.Declare micLong,"GetPixel","gdi32","GetPixel",micLong,micLong,micLong

Extern.Declare micLong,"GetWindowDC","user32","GetWindowDC",micLong

Extern.Declare micLong,"ReleaseDC","user32","ReleaseDC",micLong,micLong

Extern.Declare micLong,"GetDC","user32","GetDC",micLong

Extern.Declare micLong,"SetForegroundWindow","user32","SetForegroundWindow",micLong

Dim hDCSource

Dim hWndSource

Dim backColor

‘获取文本框句柄

hWndSource = Window("Window").WinEdit("MandatoryField1").GetROProperty("hwnd")

‘将该窗口置于前端,这是使用GetPixel的前提,它只在该像素可见时才有用

extern.SetForegroundWindow hWndSource

‘获取控件的句柄

hDCSource = Clng(Extern.GetDC(hWndSource))

‘获取控件的(11)点的背景色

backColor = Clng(Extern.GetPixel(hDCSource, Clng(1),Clng(1)))

MsgBox backColor

‘释放控件句柄

Extern.ReleaseDC hWndSource, hDCSource

Problem 17-5. 怎样通过API模拟键盘操作?

‘声明keybd_event函数

Extern.Declare micVoid, “keybd_event”, “user32”, “keybd_event”, micByte, micByte, micLong, micLong

Const KEYEVENTF_EXTENDENKEY = &H1

Const KEYEVENTF_KEYUP = &H2

Const KEYEVENTF_ KEYDOWN = &H0

Sub KeyDown (KeyAscii)

keyCode = extern.MapVirtualKey (KeyAscii, 0)

‘按下键

Extern.keybd_event KeyAscii, keyCode, Const KEYEVENTF_ KEYDOWN, 0

End Sub

Sub KeyUp (KeyAscii)

keyCode = extern.MapVirtualKey (KeyAscii, 0)

‘松开键

Extern.keybd_event KeyAscii, keyCode, Const KEYEVENTF_ KEYUP, 0

End Sub

Sub KeyPress (KeyAscii)

KeyDown KeyAscii

KeyUp KeyAscii

End Sub

你可以对计算器工具使用上述代码,观看效果。

‘声明常量来表示对应的键

Const vbKey1 = 49

Const vbKey2 = 50

Const vbKeyAdd = 107

Const vbKeyReturn = 13

Systemutil.Run “calc.exe”

Window(“title:=Claculator”).Activate

Call KeyPress (vbKey1)

Call KeyPress (vbKey2)

Call KeyPress (vbKeyAdd)

Call KeyPress (vbKeyReturn)

上述代码可以模拟同时按下 CTRL + ALT + S

‘声明常量来表示对应的键

Const vbKeyControl = 17

Const vbKeyAlt = 18

Const vbKeyS = 83

Call KeyDown (vbKeyControl)

Call KeyDown (vbKeyAlt)

Call KeyDown (vbKeyS)

Call KeyUp (vbKeyS)

Call KeyUp (vbKeyAlt)

Call KeyUp (vbKeyControl)

Problem 17-6. 怎样防止屏幕保护程序的触发?

有时我们需要在无人值守的情况下运行测试脚本,这时屏幕保护程序就会将电脑锁住,导致QTP脚本无法正常执行,我们可以通过运行下列代码来模拟键盘或鼠标操作来避免这种情况。

‘C:\PreventPCLock.vbs

Const micVoid = 0

Const micByte = 26

Const micLong = 3

Const KEYEVENTF_KEYUP = &H2

‘创建Extern对象

Set Extern = CreateObiect (“Mercury.ExternObj”)

Extern.Declare micVoid, “keybd_event”, “user32”, “keybd_event”, micByte, micByte, micLong, micLong

Extern.Declare micVoid, “Sleep”, “kernal32”, “Sleep”, micLong

While True

Extern.keybd_event 0, 0, KEYEVENTF_KEYUP, 0

Extern.Sleep 20000

Wend

注意这个代码可能会产生一个无法找到指定模块的错误。这是DLL中的这些类在注册表信息中的路径不完整导致的。将QTPBin文件夹添加到WindowsPATH环境变量中即可解决此问题。

我们也可以使用下述代码来将Bin文件夹添加到PATH环境变量中:

‘函数功能:将一个地址添加到PATH环境变量中

Public Function AddToSystemPath(ByVal Path)

Set objWMIService = GetObject(“winmgmts:\\.\root\cimv2”)

‘获取Windows环境变量

Set colItems = objWMIService.ExecQuery (“Select * from Win32_Environment where Name = ‘Path’”)

For each objItem of colItems

‘Add to PATH only if the path does not already exists

If InStr(objItem.VariableValue, PATH) = 0 Then

‘If path does not exist already then add it

strPath = objItem.VariableValue & “;” & Path

objItem.VariableValue = strPath

objItem.Put_

End If

Next

End Functino

AddToSystemPath “C:\Program Files\Mercury Interactive\QuickTest Professional\bin”

keybd_event函数发送0可以触发一个没有按键的键盘事件。读者可以讲上述代码保存到vbs文件中执行。

Problem 17-7. 怎样使一个窗口或浏览器最大化?

‘声明

Private Const SW_MAXIMIZE = 3

Extern.Declare micLong, “ShowWindow”, “user32.dll”, “ShowWindow”, micHwnd, micLong

‘想要最大化一个窗口必先先获取它的句柄

hWndWindow = Browser(“creationtime:=0”).GetROProperty(“hwnd”)

‘最大化该窗口

Extern.ShowWindow hWndWindow, SW_MAXIMIZE

Problem 17-8. 怎样通过URL下载文件到本机?

‘声明函数 URLDownLoadToFile Lib “urlmon” Alias “URLDownloadToFileA”

‘(ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, 

‘ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long

Extern.Declare micLong, “URLDownloadToFile”, “urlmon”, “URLDownloadToFIleA”, micLong, micString, micString, micLong, micLong

sSourceURL = “http://mysite/logo.gif

sTargetFile = “C:\logo.gif”

Extern.URLDownloadToFile 0, sSourceURL, sTargetFile, 0, 0

QTP API的局限性

由于VBScript只支持variant数据类型,我们调用API函数时会碰到许多限制,本节中我们将对其应变方法进行探讨。

VB6中使用API函数调用 COM 对象

GetCursorPos函数的作用是通过点阵来获取鼠标当前的坐标,我们首先在VB6中依照下述方法创建一个COM对象然后利用它在QTP中执行GetCursorPos函数。 

Image

打开VB6 ,创建一个空的 “ActiveX DLL” 工程,如图17-1

创建成功后,工程会自动生成一个类,我们可以给工程和类起一个恰当的名字,这里我们将工程命名为”WindowsAPI”,将类命名为”API”。

打开Visual Studio的 API查看器,并添加要用到的API函数 (GetCursorPos) 及结构 (POINTAPI) ,如图17-2所示。

Image

然后我们将下列定义拷贝到一个文件中。

‘声名API函数

Publick Declare Function GetCursorPos Lib “user32” Alias “GetCursorPos” (lpPoint As POINTAPI) As Long

Public Type POINTAPI

X As Long

Y As Long

End Type

我们继续在类中创建一个函数来执行GetCursorPos函数并返回执行结果。这个函数有两个参数XU,都是按地址传递的,它将执行GetCursorPos函数并将返回值分别赋给XY。我们将下面的代码添加到类中(注意要将参数声名为variant类型,不然QTP在调用该函数时会抛出一个”类型不匹配”的异常):

‘声名函数,变量

Option Explicit

Public Function GetCursorPosition (ByRef x As Variant, ByRef y As Variant) As Long

Dim lpPoint As POINTAPI

GetCursorPosition = GetCursorPos(lpPoint)

X = lpPoint.x

Y = lpPoint.y

End Function

我们还需要为其他的API添加函数,一切完成之后再生成一个DLL库文件。点击 文件à生成WIndowsAPI.dll 并将其保存:

Image

如果想要使用这些函数,我们还需要将DLL文件在系统中注册一下。将DLL文件拷贝到我们要用的机器上,然后用REGSVR32 命令注册。如下所示:

Image

QTP中调用我们的DLL文件,还需要创建这个DLL的对象。CreateObject方法需要传入形为”<ProjectName>.<ClassName>“的参数,如下所示:

‘创建COM对象

Set winAPI = CreateObject(“WindowsAPI.API”)

Dim x,y

winAPI.GetCursorPosition x,y

Msgbox “(“ & x & “,” & y & “)”

这个方法也有其不足之处:

l 使用本方法生成的DLL文件的电脑都需要复制并注册该DLL文件,这需要用户具有管理员权限。

l ActiveX 对象有时会显示”ActiveX组件无法创建对象”的信息。 

l VB6或其他工具,想要创建一个自定义的COM库的话,通常购买需要一个额外的许可证。 

通过Excel执行API 

本节我们探讨的方法,比之上节要简单一些,因为大多数的电脑中都安装了Excel。本方法是将所需函数嵌入到Excel工作簿中并通过ExcelCOM API来调用。

按照下述步骤在一个Excel工作簿中嵌入一个与上节示例相同功能的COM库:

新建一个Excel文件,同时按下ALT + F11打开VBA编辑器,或在Tools à Marco à Visual Basic Editor 中将其打开。

添加一个模块,然后将下列代码拷贝到模块中:

‘声明GetCursorPos 函数

Public Declare Function GetCursorPos lib “user32” Alias “GetCursorPos” (lpPoint As POINTAPI) As Long

Public Type POINTAPI

X As Long

Y As Long

End Type

在”ThisWorkbook”工作表中写入我们要执行的程序,如图17-5所示。

Option Explicit

Public Function GetCursorPosition (ByRef x As Variant, ByRef y As Variant) As Long

Dim lpPoint As POINTAPI

GetCursorPosition = GetCursorPos(lpPoint)

X = lpPoint.x

Y=lpPoint.y

End Function

Image

保存工作表,本例中我们将其保存为C:\APICode.xls

QTP中输入下述代码并执行,可调用我们保存在Excel中的函数:

‘打开保存着API代码的工作表

Set xlsApp = CreateObject(“Excel.Application”)

Set xlsWorkBook = xlsApp.WorkBooks.open(“C:\APICode.xls”)

‘声明传入GetCursorPosition函数时按地址传递的变量

Dim x, y

‘执行”ThisWorkBool”中的函数

xlsWorkBook.GetCursorPosition x,y

Msgbox “(“ & x & “,” & y & “)”

‘关闭Excel

xlsWorkBook.close

xlsApp.quit

set xlsWorkBool = Nothing

使用Excel的好处是:

l 代码会自动添加到QTP的数据表中,这是在每台装有QTP的电脑上都可以实现的

l 无需创建一个COM

l 方便修改,而上节的方法每次修改都要重新编译

注意: 不要将宏保存到数据表(通常情况下就是该脚本下的Default.xls文件)中,因为一旦你保存测试脚本,这个工作表就会被覆盖。

有的人可能不喜欢这个方法,下一节我们将讲述一个更简单的方法,它将在程序运行时通过VBE类自动生成宏。

自动生成Excel

将下述代码保存到一个空文件中:

‘C:\MyCode.bas

Attribute VB_Name = “MyCode”

Option Explicit

Private Type POINTAPI

X As Long

Y As Long

End Type

Private Declare Function GetCursorPos lib “user32” Alias “GetCursorPos” (lpPoint As POINTAPI) As Long

Public Function GetCursorPosition (ByRef x As Variant, ByRef y As Variant) As long

Dim lpPoint As POINTAPI

GetCursorPosition = GetCursorPos(lpPoint)

X = lpPoint.x

Y = lpPoint.y

End Function

提示:我们也可以通过 FileSystemObject 在运行脚本时生成该文件

‘创建一个Excel对象

Set xlsApp = CreateObject(“Excel.Application”)

‘新增一个工作簿

Set xlsWorkBook = xlsApp.WorkBools.Add

‘获取组件

Set xlsVBCompoonents = xlsWorkBook.VBProject.VBComponents

‘导入代码

xlsVBComponents.Import “C:\MyCode.bas”

Dim x,y

‘执行宏命令

xlsApp.Run “GetCursorPosition”,x,y

Msgbox “X:=“ & x & “, Y:=“ & y

注意如果在Excel宏设置中未勾选”信任对VBA工程对象模型的访问”,上述代码在执行时会出错。在Tools à Macro à Security…àTrusted Publishers (Tab)中,勾选”信任对VBA工程对象模型的访问”前面的复选框。

Image

我们还可以通过注册表来修改Excel的安全设置:

‘创建 WSCript shell 对象

Set oShell = CreateObject(“Wscript.Shell”)

‘启用对VB的信任

oShell.RegWrite “HKLW\SOFTWARE\Microsoft\Office\11.0\Excel\Security\AccseeVBOM”, 1, “REG_DWORD”

‘取消对VB信任

oShell.RegDelete “HKLW\SOFTWARE\Microsoft\Office\11.0\Excel\Security\AccseeVBOM”

警告: 我们在代码执行结束后一定要在注册表中删除这个键,以免你的系统受到太多的威胁。

然后导入上述代码模块:

‘生成VB 组件的实例

Set xlVBComponents = xlWorkBook.VBProject.VBComponents

xlVBComponents.Import “C:\MyCode.bas”

这里还有其他的途径来生成这个模块:

方法1:

‘创建模块文件

Const vbtext_ct_StdModule = 1

Set oModule = xlWorkBook.VBPeoject.VBComponents.Add (vbtext_ct_StdModule)

oModule.Name = “MyCode”

‘读取文件中的代码

oModule.CodeModule.AddFromFile “C:\MyCode.bas”

方法2:

‘获取ThisWorkBook的实例

Set oModule = xlWorkBook.VBPeoject.VBComponents.Item (“ThisWorkBook”)

‘在一个字符串中保存代码

Dim newCode

newCode = “Public Sub TestThis(ByVal pParam as string)” + vbNewLine

newCode = newCode + “Msgbox pParam” + vbNewLine

newCode = newCode + “End Sub” + vbNewLine

‘读取字符串中的代码

oModule.CodeModule.AddFromString newCode

‘调用刚添加的功能

xlsWorkBool.Testthis “Tarun Lalwani”

注意方法2中我们可以像调用普通的函数一样来调用TestThis 函数,而在方法1中需要通过组件对象来执行代码。

本节提出的技术有以下优点:

l 摆脱了QTP/VBS的限制,可以使用任何API函数及VBA支持的功能

l 为QTP自动化框架指出了新的方向

l 可以用来创建带有可选参数的函数

模式对话框

当被测程序在一个模式对话框需要关闭时,QTP脚本就无法继续执行,我们看一下下面的这个HTML页面:

<HTML>

<BODY>

<P>Select the file 

<INPUT type=file name=mod>

</BODY>

</HTML>

现在让我们在这个页面尝试一下下述QTP代码:

‘通过DOM点击

Browser(“creationtime:=0”).Page(“micClass:=Page”).WebFile(“name:=mod”).object.click

Msgbox “2”

QTP会无限期等待,直到click事件完成后才会执行msgbox语句。这时由于并没有出错,场景恢复功能是不起作用的。在这种情况下,我们必须通过手动、或外部程序来关闭该对话框,或者模拟鼠标事件来完成点击。

下面让我们看看是否能通过API来解决这个问题。

FindWindow

本函数用来获取指定窗口的句柄T

FindWindowEx

本函数用来获取指定窗口的子对象的句柄,如获取一个消息框的一个按钮的句柄。

PostMessage

本函数用来向指定窗口的消息队列中添加信息

SetActiveWindow

本函数用来激活一个窗口并置于桌面顶层。

现在,我们在下面的代码中结合这些API函数来关闭一个非QTP的消息框。我们先根据标题获取一个对话框的句柄,然后获取它的关闭按钮的句柄,最后发送一个消息来点击这个按钮:

‘声明API函数

Extern.Declare micLong, “Findwindow”, “user32.dll”, “FindWindowA”, micString, micString

Extern.Declare micLong, “FindwindowEx”, “user32.dll”, “FindWindowExA”, micLong, micLong, micString, micString

Extern.Declare micLong, “PostMessage”, “user32.dll”, “PostMessage A”, micLong, micLong, micLong, micRef + micLong

Extern.Declare micLong, “SetActiveWindow”, “user32.dll”, “SetActiveWindow”, micLong

‘定义一个常量来模拟点击按钮

Private Const BN_CLICK = 245

‘这里要注意窗口及按钮名称都是大小写敏感的

sWindowName = “Test Window”

sWindowButton = “OK”

‘根据窗口标题获取其句柄

hwndWindow = Extern.FindWindow (vbNullString, SWindowName)

‘如果返回的句柄非零

If hwndWindow Then

‘获取窗口中按钮的句柄

hwndButton = Extern.FindeWindowEx(hwndWindow, 0, vbNullString, sWindowButton)

If hwndButton Then

Msgbox “Got the button, activating the window and clicking the button”

‘激活窗口

Extern.SetActiveWindow hwndWindwo

‘连续发送2BN_CLICK信息,因为有时第一条信息会丢失

Extern.PostMessage hwndButton, BN_CLICK, 0, 0

Extern.PostMessage hwndButton, BN_CLICK, 0, 0

Else

Msgbox “Cannot find the button”

End If

Else

Msgbox “Cannot find the window”

End If

大家可以创建一个包含以下代码的VBS文件来测试上述代码:

‘使用msgbox函数创建一个模式对话框

Msgbox “Kill me now?”, “vbOKOnly”, “Test Window”

先执行这个脚本弹出一个消息框,然后再执行我们的QTP脚本,观察消息框有没有被关掉。 本方法还是有一些缺陷:

l 代码通过QTP执行,所以在执行时我们不能做任何事。

l 如果我们不能保证一定会有窗口出现,这段代码就有问题

l 不能用来关闭QTP产生的对话框

要避免上述问题,我们需要把代码写到VBS文件中,然后再QTP中用WSH Shell对象来调用。这意味着我们将离开QTP的环境来执行代码,所以Extern对象不再适用。由于QTP Extern对象是”Mercury.ExtrenObj”的COM对象,只要我们在机器上安装了QTP,我们就可以在VBS文件中重写这个功能。因为这个方法是独立于QTP之外的,我们还需要QTP执行时必需的参数:

‘C:\AutoClick.vbs:

Const micLong = 3

Const micString = 8

Const micByRef = 32768

Set Extern = CreateObject(“Mercury.ExternObj”)

Extern.Declare micLong, “Findwindow”, “user32.dll”, “FindWindowA”, micString, micString

Extern.Declare micLong, “FindwindowEx”, “user32.dll”, “FindWindowExA”, micLong, micLong, micString, micString

Extern.Declare micLong, “PostMessage”, “user32.dll”, “PostMessage A”, micLong, micLong, micLong, micRef + micLong

Extern.Declare micLong, “SetActiveWindow”, “user32.dll”, “SetActiveWindow”, micLong

‘定义一个常量来模拟点击按钮

Private Const BN_CLICK = 245

‘这里要注意窗口及按钮名称都是大小写敏感的

‘获取目的窗口及按钮的名称

sWindowName = WScript.Arguments(0)

sWidowButton = Wscript.Arguments(1)

‘直到窗口被点击后,再开始执行脚本

While Not AutoClickButton(sWindowName, sWindowButton)

Wend

Set Extern = Nothing

Private Function AutoClickButton(ByVal sWindowName, ByVal sWindowButton)

AutoClickButton = False

‘获取窗口句柄

hwndWindow = Extern.FindWindow (vbNullString, sWindowName)

If hwndWindow Then

‘获取窗口中按钮的句柄

hwndButton = Extern.FindeWindowEx(hwndWindow, 0, vbNullString, sWindowButton)

If hwndButton Then

‘激活窗口

Extern.SetActiveWindow hwndWindwo

‘连续发送2BN_CLICK信息,因为有时第一条信息会丢失

Extern.PostMessage hwndButton, BN_CLICK, 0, 0

Extern.PostMessage hwndButton, BN_CLICK, 0, 0

End If

End If

End Function

现在让我们执行一下下述QTP脚本:

‘创建shell对象

Set WshShell = CreateObject(“Wscript.Shell”)

‘用双引号将字符串括起来。如果不这样做,”Test Window”就会被认为是TestWindow‘个参数而不是一个字符串。

sWindowName = ““““ & “Test Window” & ““““

sWindowButton = ““““ & “OK” & ““““

Return = WshShell.Run(“C:\AutoClick.vbs “ & sWindowName & “ “ & sWindowButton, 1, False)

Set WshShell = Nothing

Msgbox “Kill me now?”, vbOKOnly, “Test Window”

注意:这里不会对WScript的工作方式进行阐述,若想获知更多细节请参考VBScript的参考手册。

执行上述代码,消息框就会自动关闭。我们的代码是一次性的因为一旦它成功关掉一个窗口就就会结束。存在多个窗口需要关闭时,可用下述代码多次调用此功能:

初始化自动点击脚本

Ret = WshShell.Run(“C:\AutoClick.vbs “ & sWindowName & “ “ & sWindowButton, 1, False)

此方法也可用于关闭Outlook的安全警告对话框。

原创粉丝点击