vapir-ie 1.7.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +437 -0
- data/LICENSE.txt +41 -0
- data/README.txt +0 -0
- data/lib/vapir-ie.rb +44 -0
- data/lib/vapir-ie/AutoItX.chm +0 -0
- data/lib/vapir-ie/AutoItX3.dll +0 -0
- data/lib/vapir-ie/IEDialog/Release/IEDialog.dll +0 -0
- data/lib/vapir-ie/autoit.rb +13 -0
- data/lib/vapir-ie/close_all.rb +32 -0
- data/lib/vapir-ie/container.rb +51 -0
- data/lib/vapir-ie/element.rb +376 -0
- data/lib/vapir-ie/elements.rb +9 -0
- data/lib/vapir-ie/form.rb +8 -0
- data/lib/vapir-ie/frame.rb +24 -0
- data/lib/vapir-ie/ie-class.rb +880 -0
- data/lib/vapir-ie/ie-process.rb +60 -0
- data/lib/vapir-ie/image.rb +59 -0
- data/lib/vapir-ie/input_elements.rb +158 -0
- data/lib/vapir-ie/link.rb +23 -0
- data/lib/vapir-ie/logger.rb +21 -0
- data/lib/vapir-ie/modal_dialog.rb +224 -0
- data/lib/vapir-ie/non_control_elements.rb +77 -0
- data/lib/vapir-ie/page_container.rb +203 -0
- data/lib/vapir-ie/process.rb +22 -0
- data/lib/vapir-ie/screen_capture.rb +7 -0
- data/lib/vapir-ie/scripts/select_file.rb +79 -0
- data/lib/vapir-ie/table.rb +33 -0
- data/lib/vapir-ie/version.rb +5 -0
- data/lib/vapir-ie/win32ole.rb +45 -0
- data/lib/vapir-ie/win32ole/win32ole.so +0 -0
- data/lib/vapir/ie.rb +1 -0
- metadata +130 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
module Vapir
|
2
|
+
|
3
|
+
class IE::Pre < IE::Element
|
4
|
+
include Vapir::Pre
|
5
|
+
end
|
6
|
+
|
7
|
+
class IE::P < IE::Element
|
8
|
+
include Vapir::P
|
9
|
+
end
|
10
|
+
|
11
|
+
# this class is used to deal with Div tags in the html page. http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/div.asp?frame=true
|
12
|
+
# It would not normally be created by users
|
13
|
+
class IE::Div < IE::Element
|
14
|
+
include Vapir::Div
|
15
|
+
end
|
16
|
+
|
17
|
+
# this class is used to deal with Span tags in the html page. It would not normally be created by users
|
18
|
+
class IE::Span < IE::Element
|
19
|
+
include Vapir::Span
|
20
|
+
end
|
21
|
+
|
22
|
+
class IE::Map < IE::Element
|
23
|
+
include Vapir::Map
|
24
|
+
end
|
25
|
+
|
26
|
+
class IE::Area < IE::Element
|
27
|
+
include Vapir::Area
|
28
|
+
end
|
29
|
+
|
30
|
+
# Accesses Label element on the html page - http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/label.asp?frame=true
|
31
|
+
class IE::Label < IE::Element
|
32
|
+
include Vapir::Label
|
33
|
+
end
|
34
|
+
|
35
|
+
class IE::Li < IE::Element
|
36
|
+
include Vapir::Li
|
37
|
+
end
|
38
|
+
class IE::Ul < IE::Element
|
39
|
+
include Vapir::Ul
|
40
|
+
end
|
41
|
+
class IE::Ol < IE::Element
|
42
|
+
include Vapir::Ol
|
43
|
+
end
|
44
|
+
class IE::H1 < IE::Element
|
45
|
+
include Vapir::H1
|
46
|
+
end
|
47
|
+
class IE::H2 < IE::Element
|
48
|
+
include Vapir::H2
|
49
|
+
end
|
50
|
+
class IE::H3 < IE::Element
|
51
|
+
include Vapir::H3
|
52
|
+
end
|
53
|
+
class IE::H4 < IE::Element
|
54
|
+
include Vapir::H4
|
55
|
+
end
|
56
|
+
class IE::H5 < IE::Element
|
57
|
+
include Vapir::H5
|
58
|
+
end
|
59
|
+
class IE::H6 < IE::Element
|
60
|
+
include Vapir::H6
|
61
|
+
end
|
62
|
+
class IE::Dl < IE::Element
|
63
|
+
include Vapir::Dl
|
64
|
+
end
|
65
|
+
class IE::Dt < IE::Element
|
66
|
+
include Vapir::Dt
|
67
|
+
end
|
68
|
+
class IE::Dd < IE::Element
|
69
|
+
include Vapir::Dd
|
70
|
+
end
|
71
|
+
class IE::Strong < IE::Element
|
72
|
+
include Vapir::Strong
|
73
|
+
end
|
74
|
+
class IE::Em < IE::Element
|
75
|
+
include Vapir::Em
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'vapir-ie/container'
|
2
|
+
|
3
|
+
module Vapir
|
4
|
+
# A PageContainer contains an HTML Document. In other words, it is a
|
5
|
+
# what JavaScript calls a Window.
|
6
|
+
#
|
7
|
+
# this assumes that document_object is defined on the includer.
|
8
|
+
module IE::PageContainer
|
9
|
+
# Used internally to determine when IE has finished loading a page
|
10
|
+
# http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowserreadystate.aspx
|
11
|
+
# http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.readystate.aspx
|
12
|
+
module WebBrowserReadyState
|
13
|
+
Uninitialized = 0 # No document is currently loaded.
|
14
|
+
Loading = 1 # The control is loading a new document.
|
15
|
+
Loaded = 2 # The control has loaded and initialized the new document, but has not yet received all the document data.
|
16
|
+
Interactive = 3 # The control has loaded enough of the document to allow limited user interaction, such as clicking hyperlinks that have been displayed.
|
17
|
+
Complete = 4 # The control has finished loading the new document and all its contents.
|
18
|
+
end
|
19
|
+
READYSTATE_COMPLETE = WebBrowserReadyState::Complete
|
20
|
+
|
21
|
+
def containing_object
|
22
|
+
document_object
|
23
|
+
end
|
24
|
+
include IE::Container
|
25
|
+
include Vapir::Exception
|
26
|
+
|
27
|
+
# This method checks the currently displayed page for http errors, 404, 500 etc
|
28
|
+
# It gets called internally by the wait method, so a user does not need to call it explicitly
|
29
|
+
def check_for_http_error
|
30
|
+
# check for IE7
|
31
|
+
n = self.document.invoke('parentWindow').navigator.appVersion
|
32
|
+
m=/MSIE\s(.*?);/.match( n )
|
33
|
+
if m and m[1] =='7.0'
|
34
|
+
if m = /HTTP (\d\d\d.*)/.match( self.title )
|
35
|
+
raise NavigationException, m[1]
|
36
|
+
end
|
37
|
+
else
|
38
|
+
# assume its IE6
|
39
|
+
url = self.document.location.href
|
40
|
+
if /shdoclc.dll/.match(url)
|
41
|
+
m = /id=IEText.*?>(.*?)</i.match(self.html)
|
42
|
+
raise NavigationException, m[1] if m
|
43
|
+
end
|
44
|
+
end
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def document_element
|
49
|
+
document_object.documentElement
|
50
|
+
end
|
51
|
+
def content_window_object
|
52
|
+
document_object.parentWindow
|
53
|
+
end
|
54
|
+
|
55
|
+
def page_container
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# The HTML of the current page
|
60
|
+
def html
|
61
|
+
document_element.outerHTML
|
62
|
+
end
|
63
|
+
|
64
|
+
# The url of the page object.
|
65
|
+
def url
|
66
|
+
document_object.location.href
|
67
|
+
end
|
68
|
+
|
69
|
+
# The text of the current page
|
70
|
+
def text
|
71
|
+
document_element.innerText
|
72
|
+
end
|
73
|
+
|
74
|
+
def close
|
75
|
+
content_window_object.close
|
76
|
+
end
|
77
|
+
|
78
|
+
def title
|
79
|
+
document_object.title
|
80
|
+
end
|
81
|
+
|
82
|
+
# Execute the given JavaScript string
|
83
|
+
def execute_script(source)
|
84
|
+
retried=false
|
85
|
+
result=nil
|
86
|
+
begin
|
87
|
+
result=document_object.parentWindow.eval(source)
|
88
|
+
rescue WIN32OLERuntimeError
|
89
|
+
# don't retry more than once; don't catch anything but the particular thing we're looking for
|
90
|
+
if retried || $!.message !~ /unknown property or method:? `eval'/
|
91
|
+
raise
|
92
|
+
end
|
93
|
+
# this can happen if no scripts have executed at all - the 'eval' function doesn't exist.
|
94
|
+
# execScript works, but behaves differently than eval (it doesn't return anything) - but
|
95
|
+
# once an execScript has run, eval is subsequently defined. so, execScript a blank script,
|
96
|
+
# and then try again with eval.
|
97
|
+
document.parentWindow.execScript('null')
|
98
|
+
retried=true
|
99
|
+
retry
|
100
|
+
end
|
101
|
+
return result
|
102
|
+
end
|
103
|
+
|
104
|
+
# Block execution until the page has loaded.
|
105
|
+
# =nodoc
|
106
|
+
# Note: This code needs to be prepared for the ie object to be closed at
|
107
|
+
# any moment!
|
108
|
+
def wait(options={})
|
109
|
+
unless options.is_a?(Hash)
|
110
|
+
raise ArgumentError, "given options should be a Hash, not #{options.inspect} (#{options.class})\nold conflicting arguments of no_sleep or last_url are gone"
|
111
|
+
end
|
112
|
+
options={:sleep => false, :interval => 0.1, :timeout => 120}.merge(options)
|
113
|
+
@xml_parser_doc = nil
|
114
|
+
@down_load_time = nil
|
115
|
+
start_load_time = Time.now
|
116
|
+
|
117
|
+
if respond_to?(:browser_object)
|
118
|
+
::Waiter.try_for(options[:timeout]-(Time.now-start_load_time), :interval => options[:interval], :exception => "The browser was still busy at the end of the specified interval") do
|
119
|
+
return unless exists?
|
120
|
+
!browser_object.busy
|
121
|
+
end
|
122
|
+
::Waiter.try_for(options[:timeout]-(Time.now-start_load_time), :interval => options[:interval], :exception => "The browser's readyState was still not ready for interaction at the end of the specified interval") do
|
123
|
+
return unless exists?
|
124
|
+
[WebBrowserReadyState::Interactive, WebBrowserReadyState::Complete].include?(browser_object.readyState)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
::Waiter.try_for(options[:timeout]-(Time.now-start_load_time), :interval => options[:interval], :exception => "The browser's document was still not defined at the end of the specified interval") do
|
128
|
+
return unless exists?
|
129
|
+
document_object
|
130
|
+
end
|
131
|
+
urls=[]
|
132
|
+
all_frames_complete_result=::Waiter.try_for(options[:timeout]-(Time.now-start_load_time), :interval => options[:interval], :exception => nil, :condition => proc{|result| result==true }) do
|
133
|
+
return unless exists?
|
134
|
+
all_frames_complete?(document_object, urls)
|
135
|
+
end
|
136
|
+
case all_frames_complete_result
|
137
|
+
when false
|
138
|
+
raise "A frame on the browser did not come into readyState complete by the end of the specified interval"
|
139
|
+
when Exception
|
140
|
+
message = "A frame on the browser encountered an error.\n"
|
141
|
+
if all_frames_complete_result.message =~ /0x80070005/
|
142
|
+
message += "An 'Access is denied' error might be fixed by adding the domain of the site to your 'Trusted Sites'.\n"
|
143
|
+
end
|
144
|
+
message+="Original message was:\n\n"
|
145
|
+
message+=all_frames_complete_result.message
|
146
|
+
raise all_frames_complete_result.class, message, all_frames_complete_result.backtrace
|
147
|
+
when true
|
148
|
+
# dandy; carry on.
|
149
|
+
else
|
150
|
+
# this should never happen.
|
151
|
+
raise "Unexpected result from all_frames_complete?: #{all_frames_complete_result.inspect}"
|
152
|
+
end
|
153
|
+
@url_list=(@url_list || [])+urls
|
154
|
+
|
155
|
+
@down_load_time= Time.now - start_load_time
|
156
|
+
run_error_checks if respond_to?(:run_error_checks)
|
157
|
+
sleep @pause_after_wait if options[:sleep]
|
158
|
+
@down_load_time
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
# this returns true if all frames are complete.
|
163
|
+
# it returns false if a frame is incomplete.
|
164
|
+
# if an unexpected exception is encountered, it returns that exception. yes, returns, not raises,
|
165
|
+
# due to the fact that an exception may indicate either the frame not being complete, or an actual
|
166
|
+
# error condition - it is difficult to differentiate. in the usage above, in #wait, we check
|
167
|
+
# if an exception is still being raised at the end of the specified interval, and raise it if so.
|
168
|
+
# if it stops being raised, we carry on.
|
169
|
+
def all_frames_complete?(document, urls=nil)
|
170
|
+
begin
|
171
|
+
if urls && !urls.include?(document.location.href)
|
172
|
+
urls << document.location.href
|
173
|
+
end
|
174
|
+
frames=document.frames
|
175
|
+
return document.readyState=='complete' && (0...frames.length).all? do |i|
|
176
|
+
frame=document.frames[i.to_s]
|
177
|
+
frame_document=begin
|
178
|
+
frame.document
|
179
|
+
rescue WIN32OLERuntimeError
|
180
|
+
$!
|
181
|
+
end
|
182
|
+
case frame_document
|
183
|
+
when nil
|
184
|
+
# frame hasn't loaded to the point where it has a document yet
|
185
|
+
false
|
186
|
+
when WIN32OLE
|
187
|
+
# frame has a document - check recursively
|
188
|
+
all_frames_complete?(frame_document, urls)
|
189
|
+
when WIN32OLERuntimeError
|
190
|
+
# if we get a WIN32OLERuntimeError with access denied, that is probably a 404 and it's not going
|
191
|
+
# to load, so no reason to keep waiting for it - consider it 'complete' and return true.
|
192
|
+
# there's probably a better method of determining this but I haven't found it yet.
|
193
|
+
true
|
194
|
+
else # don't know what we'd have here
|
195
|
+
raise RuntimeError, "unknown frame.document: #{frame_document.inspect} (#{frame_document.class})"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
rescue WIN32OLERuntimeError
|
199
|
+
return $!
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end # module
|
203
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'vapir-ie/ie-class'
|
2
|
+
|
3
|
+
module Vapir
|
4
|
+
module Process
|
5
|
+
|
6
|
+
# Returns the number of windows processes running with the specified name.
|
7
|
+
def self.count name
|
8
|
+
mgmt = WIN32OLE.connect('winmgmts:\\\\.')
|
9
|
+
processes = mgmt.InstancesOf('win32_process')
|
10
|
+
processes.extend Enumerable
|
11
|
+
processes.select{|x| x.name == name}.length
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class IE
|
17
|
+
# Returns the number of IEXPLORE processes currently running.
|
18
|
+
def self.process_count
|
19
|
+
Vapir::Process.count 'iexplore.exe'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
at_exit do
|
2
|
+
if $!
|
3
|
+
err={:class => $!.class, :message => $!.message, :backtrace => $!.backtrace}
|
4
|
+
if $upload_dialog && $upload_dialog.exists?
|
5
|
+
# if the upload dialog still exists, we need to close it so that the #click WIN32OLE call in the parent process can return.
|
6
|
+
if (upload_dialog_popup=$upload_dialog.enabled_popup)
|
7
|
+
# we will assume this popup is of the "The above file name is invalid" variety, so click 'OK'
|
8
|
+
contents=begin
|
9
|
+
upload_dialog_popup.children.select{|child| child.class_name=='Static' && child.text!=''}.map{|child| child.text}.join(' ')
|
10
|
+
rescue WinWindow::Error
|
11
|
+
"{Unable to retrieve contents}"
|
12
|
+
end
|
13
|
+
err[:message]+="\n\nA popup was found on the dialog with title: \n#{upload_dialog_popup.text.inspect}\nand text contents: \n#{contents}"
|
14
|
+
if upload_dialog_popup.click_child_button_try_for!('OK', 4, :exception => nil)
|
15
|
+
err[:message]+="\n\nClicked 'OK' on the popup."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
# once the popup is gone (or if there wasn't one - this happens if the field was set to blank, or set to a directory that exists)
|
19
|
+
# then click 'cancel'
|
20
|
+
if $upload_dialog.click_child_button_try_for!('Cancel', 4, :exception => nil)
|
21
|
+
err[:message]+="\n\nClicked the 'Cancel' button instead of 'Open' on the File Upload dialog."
|
22
|
+
end
|
23
|
+
|
24
|
+
# none of the above are expected to error, but maybe should be in a begin/rescue just in case?
|
25
|
+
end
|
26
|
+
if $error_file_name && $error_file_name != ''
|
27
|
+
File.open($error_file_name, 'w') do |error_file|
|
28
|
+
to_write=Marshal.dump(err)
|
29
|
+
error_file.write(to_write)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
else
|
33
|
+
if $error_file_name && $error_file_name != '' && File.exists?($error_file_name)
|
34
|
+
File.delete($error_file_name)
|
35
|
+
# the file not existing indicates success.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'vapir-common', 'lib'))
|
41
|
+
|
42
|
+
require 'vapir-common/win_window'
|
43
|
+
require 'vapir-common/waiter'
|
44
|
+
require 'vapir-common/exceptions'
|
45
|
+
|
46
|
+
browser_hwnd, file_path, $error_file_name=*ARGV
|
47
|
+
unless (2..3).include?(ARGV.size) && browser_hwnd =~ /^\d+$/ && browser_hwnd.to_i > 0
|
48
|
+
raise ArgumentError, "This script takes two or three arguments: the hWnd that the File Selection dialog will pop up on (positive integer); the path to the file to select; and (optional) a filename to write any failure message to."
|
49
|
+
end
|
50
|
+
|
51
|
+
# titles of file upload window titles in supported browsers
|
52
|
+
# Add to this titles in other languages, too
|
53
|
+
UploadWindowTitles= { :IE8 => "Choose File to Upload",
|
54
|
+
:IE7 => 'Choose file',
|
55
|
+
}
|
56
|
+
# list of arguments to give to WinWindow#child_control_with_preceding_label to find the filename field
|
57
|
+
# on dialogs of supported browsers (just the one right now because it's the same in ie7 and ie8)
|
58
|
+
# add to this stuff for other languages, too
|
59
|
+
UploadWindowFilenameFields = [["File &name:", {:control_class_name => 'ComboBoxEx32'}]]
|
60
|
+
|
61
|
+
browser_window=WinWindow.new(browser_hwnd.to_i)
|
62
|
+
|
63
|
+
popup=nil
|
64
|
+
$upload_dialog=::Waiter.try_for(16, :exception => nil) do
|
65
|
+
if (popup=browser_window.enabled_popup) && UploadWindowTitles.values.include?(popup.text)
|
66
|
+
popup
|
67
|
+
end
|
68
|
+
end
|
69
|
+
unless $upload_dialog
|
70
|
+
raise Vapir::Exception::NoMatchingWindowFoundException.new('No window found to upload a file - '+(popup ? "enabled popup exists but has unrecognized text #{popup.text}" : 'no popup is on the browser'))
|
71
|
+
end
|
72
|
+
filename_fields=UploadWindowFilenameFields.map do |control_args|
|
73
|
+
$upload_dialog.child_control_with_preceding_label(*control_args)
|
74
|
+
end
|
75
|
+
unless (filename_field=filename_fields.compact.first)
|
76
|
+
raise Vapir::Exception::NoMatchingWindowFoundException, "Could not find a filename field in the File Upload dialog"
|
77
|
+
end
|
78
|
+
filename_field.send_set_text! file_path
|
79
|
+
$upload_dialog.click_child_button_try_for!('Open', 4, :exception => WinWindow::Error.new("Failed to click the Open button on the File Upload dialog. It exists, but we couldn't click it."))
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'vapir-ie/element'
|
2
|
+
require 'vapir-common/elements/elements'
|
3
|
+
|
4
|
+
module Vapir
|
5
|
+
|
6
|
+
# This class is used for dealing with tables.
|
7
|
+
# Normally a user would not need to create this object as it is returned by the Vapir::Container#table method
|
8
|
+
#
|
9
|
+
# many of the methods available to this object are inherited from the Element class
|
10
|
+
#
|
11
|
+
class IE::Table < IE::Element
|
12
|
+
include Vapir::Table
|
13
|
+
|
14
|
+
def self.create_from_element(container, element)
|
15
|
+
Vapir::Table.create_from_element(container, element)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# this class is a table body
|
20
|
+
class IE::TableBody < IE::Element
|
21
|
+
include Vapir::TBody
|
22
|
+
end
|
23
|
+
|
24
|
+
class IE::TableRow < IE::Element
|
25
|
+
include Vapir::TableRow
|
26
|
+
end
|
27
|
+
|
28
|
+
# this class is a table cell - when called via the Table object
|
29
|
+
class IE::TableCell < IE::Element
|
30
|
+
include Vapir::TableCell
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# load the correct version of win32ole
|
2
|
+
|
3
|
+
# Use our modified win32ole library
|
4
|
+
|
5
|
+
if RUBY_VERSION =~ /^1\.8/
|
6
|
+
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'vapir-ie', 'win32ole'))
|
7
|
+
else
|
8
|
+
# loading win32ole from stdlib on 1.9
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
require 'win32ole'
|
13
|
+
|
14
|
+
WIN32OLE.codepage = WIN32OLE::CP_UTF8
|
15
|
+
|
16
|
+
# necessary extension of win32ole
|
17
|
+
class WIN32OLE
|
18
|
+
def respond_to?(method)
|
19
|
+
super || object_respond_to?(method)
|
20
|
+
end
|
21
|
+
|
22
|
+
# checks if WIN32OLE#ole_method returns an WIN32OLE_METHOD, or errors.
|
23
|
+
# WARNING: #ole_method is pretty slow, and consequently so is this. you are likely to be better
|
24
|
+
# off just calling a method you are not sure exists, and rescuing the WIN32OLERuntimeError
|
25
|
+
# that is raised if it doesn't exist.
|
26
|
+
def object_respond_to?(method)
|
27
|
+
method=method.to_s
|
28
|
+
# strip assignment = from methods. going to assume that if it has a getter method, it will take assignment, too. this may not be correct, but will have to do.
|
29
|
+
if method =~ /=\z/
|
30
|
+
method=$`
|
31
|
+
end
|
32
|
+
respond_to_cache[method]
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def respond_to_cache
|
37
|
+
@respond_to_cache||=Hash.new do |hash, key|
|
38
|
+
hash[key]=begin
|
39
|
+
!!self.ole_method(key)
|
40
|
+
rescue WIN32OLERuntimeError
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|