ruby+watir--watir3.0上实现快照/截图
我想在watir框架运行时,在出现错误“FAIL”状态前建立一个快照,就想到了watir的截图功能,在网上找了一些例子(参考:http://www.cnblogs.com/sky_online/archive/2009/12/13/1546252.html),但是不好使,提示“Win32API”的错误。
环境:Ruby192+watir3.0.0
gem list信息如下:
Microsoft Windows XP [版本 5.1.2600] (C) 版权所有 1985-2001 Microsoft Corp. C:\Documents and Settings\Administrator>gem list *** LOCAL GEMS *** addressable (2.2.8) builder (3.0.0) childprocess (0.3.2) commonwatir (3.0.0) cucumber (1.2.1) diff-lcs (1.1.3) ffi (1.0.11) gherkin (2.11.1) hoe (3.0.6) json (1.7.3) libwebsocket (0.1.3) mini_magick (3.2.1) minitest (1.6.0) multi_json (1.3.4) nokogiri (1.5.3 x86-mingw32) rake (0.8.7) rautomation (0.7.2) rdoc (2.5.8) rubygems-update (1.8.24) rubyzip (0.9.8) s4t-utils (1.0.4) selenium-webdriver (2.25.0, 2.21.2) subexec (0.0.4) user-choices (1.1.6.1) watir (3.0.0) watir-classic (3.0.0) watir-webdriver (0.6.1) watir-webdriver-performance (0.1.3.1) win32-api (1.4.8) win32-process (0.6.5) windows-api (0.4.1) windows-pr (1.2.1) xml-simple (1.1.1) C:\Documents and Settings\Administrator>
这个LibScreen.rb的例子适合在watir1.6.5版本的,有高手试验过。直接运行LibScreen.rb例子的话,会出现的错误,请过森破的深入分析,需要把脚本中的vkKeyScan.Call()中带问号的修改一下就可以,如下:所有带问号?的都要修改一遍,否则。。
vkKeyScan.Call(?V)
替换成
vkKeyScan.Call("V".getbyte(0))
?V输出的是ASCII 86(在低版本中),那在我的高版本中显示的是V,这就是高版本和低版本的区别。
经过验证,本地保存快照成功。并对LibScreen.rb的例子进行了功能扩展:可以保存.jpg、.bmp、.png、.gif格式的文件;有人说图片比较占用磁盘空间,在脚本中把另存为默认成PNG格式的文件(虽然和保存的格式对不上),因为占用空间小,针对这个我还专门做了对比:
class ScreenClass def initialize require "Win32API" end #功能说明: #- 将文件重命名 # #参数说明: #- from:原文件路径,必须带文件名,格式如:c:\\test.txt #- to:新文件路径及名称,必须带文件名,格式如:c:\\test_bak.txt,不带该参数时,默认在原文件名后加上日期 # #调用示例: #- $TxtClass.RenameFile("c:\\test.txt","c:\\test_bak.txt") #- $TxtClass.RenameFile("c:\\test.txt") # #返回值说明: #- 成功:返回true #- 失败:返回false def RenameFile(from,to = nil) begin if (FileTest::exist?(from)) and (File.basename(from) =~ /.*\..*/ ) if (to == nil) extname = File.extname(from) filename = File.basename(from,extname) new_filename = filename + '.' + Time.now.strftime("%Y%m%d%H%M%S") + extname to = File.dirname(from) + '/'+ new_filename end File.rename(from, to) return true else puts "重命名文件失败,原因:文件不存在,路径为#{from}" return false end rescue StandardError => bang puts "Error running script: " + bang return false end end #功能说明: #- 获取文件的真实路径 # #参数说明: #- file_path:原文件路径,如果原文件路径不存在,系统自动创建相应路径 #- return_file:是否返回路径中的文件名,默认未返回 # #调用示例: #- $TxtClass.GetRealPath("#{File.dirname(__FILE__)}/http://www.cnblogs.com/input/data.xls" ) # #返回值说明: #- 成功:返回真实的路径 #- 失败:返回false def GetRealPath(file_path,return_file = 'Y') begin @@file_name = '' @@real_dir_row = [] if (file_path.include?("\\")) file_path = file_path.to_s.gsub('\\','/') end if (file_path.include?("/")) file_basename = File.basename(file_path) #获取文件名 file_dirname = File.dirname(file_path) if (file_basename =~ /.*\..*/) file_dirname = File.dirname(file_path) else file_basename = '' file_dirname = file_path end if (!FileTest::exist?(file_dirname)) #判断目录是否存在,不存在则创建相应目录 FileUtils.makedirs(file_dirname) end if (file_dirname[0,2] == './') real_dir = Pathname.new(File.dirname(File.dirname(file_dirname[0,2]))).realpath real_path = File.join(real_dir,file_dirname[2,file_dirname.length] ) else real_path = file_dirname end if (real_path.include?("..")) temp_row = real_path.split('/') temp_row.each do |dir| if(dir == "..") @@real_dir_row.pop else @@real_dir_row.push(dir) end end real_path = @@real_dir_row.join('/') end if (return_file.upcase == 'Y') result = File.join(real_path,file_basename) else result = real_path end result = result.to_s.gsub('/','\\') return result else puts "获取文件路径失败,原因:#{real_path}路径格式不正确。" return false end rescue StandardError => bang puts "Error running script: " + bang return false end end #def GetRealPath def ScreenCapture(file_path , active_window_only=false, save_as_bmp=false) keybd_event = Win32API.new("user32", "keybd_event", ['I','I','L','L'], 'V') vkKeyScan = Win32API.new("user32", "VkKeyScan", ['I'], 'I') winExec = Win32API.new("kernel32", "WinExec", ['P','L'], 'L') openClipboard = Win32API.new("user32", "OpenClipboard", ['L'], 'I') setClipboardData = Win32API.new("user32", "SetClipboardData", ['I', 'I'], 'I') closeClipboard = Win32API.new("user32", "CloseClipboard", [], 'I') globalAlloc = Win32API.new("kernel32", "GlobalAlloc", ['I', 'I'], 'I') globalLock = Win32API.new("kernel32", "GlobalLock", ['I'], 'I') globalUnlock = Win32API.new("kernel32", "GlobalUnlock", ['I'], 'I') memcpy = Win32API.new("msvcrt", "memcpy", ['I', 'P', 'I'], 'I') file_basename = File.basename(file_path) #获取文件名 if(file_basename.upcase =~ /.*.(JPG|BMP|PNG|GIF)/) real_file_path = GetRealPath(file_path) #获取文件的真实路径 if (FileTest::exist?(real_file_path)) RenameFile(real_file_path) end if active_window_only ==false keybd_event.Call(0x2C,0,0,0) # Print Screen else keybd_event.Call(0x2C,1,0,0) # Alt+Print Screen end winExec.Call('mspaint.exe', 5) sleep(1) # Ctrl + V : Paste keybd_event.Call(0x11, 1, 0, 0) keybd_event.Call(vkKeyScan.Call("V".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("V".getbyte(0)), 1, 0X2, 0) keybd_event.Call(0x11, 1, 0X2, 0) # Alt F + A : Save As keybd_event.Call(0x12, 1, 0, 0) keybd_event.Call(vkKeyScan.Call("F".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("F".getbyte(0)), 1, 0X2, 0) keybd_event.Call(0x12, 1, 0X2, 0) keybd_event.Call(vkKeyScan.Call("A".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("A".getbyte(0)), 1, 0X2, 0) sleep(1) # copy filename to clipboard 复制 hmem = globalAlloc.Call(0X0002, real_file_path.length+1) mem = globalLock.Call(hmem) memcpy.Call(mem, real_file_path, real_file_path.length+1) globalUnlock.Call(hmem) openClipboard.Call(0) setClipboardData.Call(1, hmem) closeClipboard.Call sleep(1) # Ctrl + V : Paste粘贴 keybd_event.Call(0x11, 1, 0, 0) keybd_event.Call(vkKeyScan.Call("V".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("V".getbyte(0)), 1, 0X2, 0) keybd_event.Call(0x11, 1, 0X2, 0) #快照另存为 if save_as_bmp == false # goto the combo box keybd_event.Call(0x09, 1, 0, 0) keybd_event.Call(0x09, 1, 0X2, 0) sleep(0.5) # select the first entry with J 选择另存为的类型,这里以png格式为例,如果另存为gif格式在保存时会提示“失真的信息” keybd_event.Call(vkKeyScan.Call("P".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("P".getbyte(0)), 1, 0X2, 0) sleep(0.5) end # Enter key keybd_event.Call(0x0D, 1, 0, 0) keybd_event.Call(0x0D, 1, 0X2, 0) sleep(1) # Alt + F4 : Exit keybd_event.Call(0x12, 1, 0, 0) keybd_event.Call(0x73, 1, 0, 0) keybd_event.Call(0x73, 1, 0X2, 0) keybd_event.Call(0x12, 1, 0X2, 0) sleep(1) puts "快照保存的路径为:#{real_file_path}" return true else puts "保存的文件扩展名不正确,路径必须带文件名且文件扩展名必须为.jpg、.bmp、.png、.gif" return false end end # def ScreenCapture end end #class ScreenClass end
验证:
require "watir" require "LibScreen.rb" def test ie = Watir::IE.new ie.goto("http://www.baidu.com") #初始对象 i = ScreenClass.new #截取整个屏幕 #i.ScreenCapture("c:\\test1.png") #截取当前激活的窗口 i.ScreenCapture("c:\\test1.png",true,false) end
另外watir还有自带的截图工具,路径Ruby\lib\ruby\gems\1.9.1\gems\watir-classic-3.0.0\lib\watir-classic\screen_capture.rb,一样还是需要把所有带问号?的都要修改一遍。这个自带的我们可以直接引用:
require 'watir-classic/screen_capture' include Watir::ScreenCapture def test ie = Watir::IE.new ie.goto("http://www.baidu.com") screen_capture("c:\\123111.jpg",true,false) end
算啦 我把自带的screen_capture.rb代码沾上来吧,我已经修改过啦,可以参考:
require 'Win32API' module Watir module ScreenCapture KEYEVENTF_KEYUP = 0x2 SW_HIDE = 0 SW_SHOW = 5 SW_SHOWNORMAL = 1 VK_CONTROL = 0x11 VK_F4 = 0x73 VK_MENU = 0x12 VK_RETURN = 0x0D VK_SHIFT = 0x10 VK_SNAPSHOT = 0x2C VK_TAB = 0x09 GMEM_MOVEABLE = 0x0002 CF_TEXT = 1 # this method saves the current window or whole screen as either a bitmap or a jpeg # It uses paint to save the file, so will barf if a duplicate filename is selected, or the path doesnt exist etc # * filename - string - the name of the file to save. If its not fully qualified the current directory is used # * active_window - boolean - if true, the whole screen is captured, if false, just the active window is captured # * save_as_bmp - boolean - if true saves the file as a bitmap, saves it as a jpeg otherwise def screen_capture(filename , active_window_only=false, save_as_bmp=false) keybd_event = Win32API.new("user32", "keybd_event", ['I','I','L','L'], 'V') vkKeyScan = Win32API.new("user32", "VkKeyScan", ['I'], 'I') winExec = Win32API.new("kernel32", "WinExec", ['P','L'], 'L') openClipboard = Win32API.new('user32', 'OpenClipboard', ['L'], 'I') setClipboardData = Win32API.new('user32', 'SetClipboardData', ['I', 'I'], 'I') closeClipboard = Win32API.new('user32', 'CloseClipboard', [], 'I') globalAlloc = Win32API.new('kernel32', 'GlobalAlloc', ['I', 'I'], 'I') globalLock = Win32API.new('kernel32', 'GlobalLock', ['I'], 'I') globalUnlock = Win32API.new('kernel32', 'GlobalUnlock', ['I'], 'I') memcpy = Win32API.new('msvcrt', 'memcpy', ['I', 'P', 'I'], 'I') filename = Dir.getwd.tr('/','\\') + '\\' + filename unless filename.index('\\') if active_window_only ==false keybd_event.Call(VK_SNAPSHOT,0,0,0) # Print Screen else keybd_event.Call(VK_SNAPSHOT,1,0,0) # Alt+Print Screen end winExec.Call('mspaint.exe', SW_SHOW) sleep(1) # Ctrl + V : Paste keybd_event.Call(VK_CONTROL, 1, 0, 0) keybd_event.Call(vkKeyScan.Call("V".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("V".getbyte(0)), 1, KEYEVENTF_KEYUP, 0) keybd_event.Call(VK_CONTROL, 1, KEYEVENTF_KEYUP, 0) # Alt F + A : Save As keybd_event.Call(VK_MENU, 1, 0, 0) keybd_event.Call(vkKeyScan.Call("F".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("F".getbyte(0)), 1, KEYEVENTF_KEYUP, 0) keybd_event.Call(VK_MENU, 1, KEYEVENTF_KEYUP, 0) keybd_event.Call(vkKeyScan.Call("A".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("A".getbyte(0)), 1, KEYEVENTF_KEYUP, 0) sleep(1) # copy filename to clipboard hmem = globalAlloc.Call(GMEM_MOVEABLE, filename.length+1) mem = globalLock.Call(hmem) memcpy.Call(mem, filename, filename.length+1) globalUnlock.Call(hmem) openClipboard.Call(0) setClipboardData.Call(CF_TEXT, hmem) closeClipboard.Call sleep(1) # Ctrl + V : Paste keybd_event.Call(VK_CONTROL, 1, 0, 0) keybd_event.Call(vkKeyScan.Call("V".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("V".getbyte(0)), 1, KEYEVENTF_KEYUP, 0) keybd_event.Call(VK_CONTROL, 1, KEYEVENTF_KEYUP, 0) if save_as_bmp == false # goto the combo box keybd_event.Call(VK_TAB, 1, 0, 0) keybd_event.Call(VK_TAB, 1, KEYEVENTF_KEYUP, 0) sleep(0.5) # select the first entry with J keybd_event.Call(vkKeyScan.Call("J".getbyte(0)), 1, 0, 0) keybd_event.Call(vkKeyScan.Call("J".getbyte(0)), 1, KEYEVENTF_KEYUP, 0) sleep(0.5) end # Enter key keybd_event.Call(VK_RETURN, 1, 0, 0) keybd_event.Call(VK_RETURN, 1, KEYEVENTF_KEYUP, 0) sleep(1) # Alt + F4 : Exit keybd_event.Call(VK_MENU, 1, 0, 0) keybd_event.Call(VK_F4, 1, 0, 0) keybd_event.Call(VK_F4, 1, KEYEVENTF_KEYUP, 0) keybd_event.Call(VK_MENU, 1, KEYEVENTF_KEYUP, 0) sleep(1) end end end