robust_excel_ole 0.5.1 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/Changelog +13 -0
  2. data/README.rdoc +70 -21
  3. data/README_detail.rdoc +60 -27
  4. data/examples/edit_sheets/example_access_sheets_and_cells.rb +2 -2
  5. data/examples/edit_sheets/example_adding_sheets.rb +2 -2
  6. data/examples/edit_sheets/example_concating.rb +2 -3
  7. data/examples/edit_sheets/example_copying.rb +2 -3
  8. data/examples/edit_sheets/example_expanding.rb +2 -3
  9. data/examples/edit_sheets/example_naming.rb +2 -3
  10. data/examples/edit_sheets/example_ranges.rb +2 -2
  11. data/examples/edit_sheets/example_saving.rb +2 -3
  12. data/examples/open_save_close/example_control_to_excel.rb +3 -3
  13. data/examples/open_save_close/example_default_excel.rb +4 -4
  14. data/examples/open_save_close/example_force_excel.rb +2 -2
  15. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +2 -2
  16. data/examples/open_save_close/example_if_obstructed_forget.rb +2 -2
  17. data/examples/open_save_close/example_if_obstructed_save.rb +2 -2
  18. data/examples/open_save_close/example_if_unsaved_accept.rb +2 -2
  19. data/examples/open_save_close/example_if_unsaved_forget.rb +2 -2
  20. data/examples/open_save_close/example_if_unsaved_forget_more.rb +4 -5
  21. data/examples/open_save_close/example_read_only.rb +2 -2
  22. data/examples/open_save_close/example_rename_cells.rb +3 -3
  23. data/examples/open_save_close/example_reuse.rb +2 -2
  24. data/examples/open_save_close/example_simple.rb +3 -4
  25. data/examples/open_save_close/example_unobtrusively.rb +2 -2
  26. data/lib/robust_excel_ole/book.rb +84 -78
  27. data/lib/robust_excel_ole/bookstore.rb +5 -1
  28. data/lib/robust_excel_ole/excel.rb +165 -188
  29. data/lib/robust_excel_ole/reo_common.rb +4 -0
  30. data/lib/robust_excel_ole/sheet.rb +15 -6
  31. data/lib/robust_excel_ole/version.rb +1 -1
  32. data/spec/book_spec.rb +104 -77
  33. data/spec/book_specs/book_close_spec.rb +9 -8
  34. data/spec/book_specs/book_misc_spec.rb +367 -26
  35. data/spec/book_specs/book_open_spec.rb +375 -94
  36. data/spec/book_specs/book_save_spec.rb +137 -112
  37. data/spec/book_specs/book_sheet_spec.rb +1 -1
  38. data/spec/book_specs/book_subclass_spec.rb +2 -1
  39. data/spec/book_specs/book_unobtr_spec.rb +87 -96
  40. data/spec/bookstore_spec.rb +8 -5
  41. data/spec/cell_spec.rb +1 -1
  42. data/spec/data/another_workbook.xls +0 -0
  43. data/spec/data/book_with_blank.xls +0 -0
  44. data/spec/data/workbook.xls +0 -0
  45. data/spec/excel_spec.rb +484 -72
  46. data/spec/range_spec.rb +1 -1
  47. data/spec/sheet_spec.rb +47 -1
  48. metadata +4 -5
@@ -28,11 +28,15 @@ module RobustExcelOle
28
28
  weakref_books = @filename2books[filename_key]
29
29
  return nil unless weakref_books
30
30
  result = open_book = closed_book = nil
31
+ weakref_books = weakref_books.map {|wr_book| wr_book if wr_book.weakref_alive? }.compact
32
+ @filename2books[filename_key] = weakref_books
31
33
  weakref_books.each do |wr_book|
32
34
  if (not wr_book.weakref_alive?)
35
+ trace "warn: this should never happen"
33
36
  begin
34
37
  @filename2books[filename_key].delete(wr_book)
35
- rescue
38
+ rescue
39
+ trace "#{$!.message}"
36
40
  trace "Warning: deleting dead reference failed: file: #{filename.inspect}"
37
41
  end
38
42
  else
@@ -2,76 +2,89 @@
2
2
 
3
3
  require 'timeout'
4
4
 
5
+ def ka
6
+ Excel.kill_all
7
+ end
8
+
9
+
5
10
  module RobustExcelOle
6
11
 
7
12
  class Excel < REOCommon
8
13
 
9
14
  attr_accessor :ole_excel
15
+ attr_accessor :visible
16
+ attr_accessor :displayalerts
10
17
 
11
18
  alias ole_object ole_excel
12
19
 
13
20
  @@hwnd2excel = {}
14
21
 
15
22
  # creates a new Excel instance
23
+ # @param [Hash] options the options
24
+ # @option options [Variant] :displayalerts
25
+ # @option options [Boolean] :visible
16
26
  # @return [Excel] a new Excel instance
17
- def self.create
18
- new(:reuse => false)
27
+ def self.create(options = {})
28
+ new(options.merge({:reuse => false}))
19
29
  end
20
30
 
21
- # returns (connects to) the current Excel instance, if such a running Excel instance exists
22
- # more specific: connects to the first opened Excel instance
23
- # if this Excel instance is being closed, then Excel creates a new Excel instance that has the same Hwnd
24
- # creates a new one, otherwise
31
+ # returns (connects to) the current (first opened) Excel instance, if such a running Excel instance exists
32
+ # returns a new Excel instance, otherwise
33
+ # @option options [Variant] :displayalerts
34
+ # @option options [Boolean] :visible
25
35
  # @return [Excel] an Excel instance
26
- def self.current
27
- new(:reuse => true)
36
+ def self.current(options = {})
37
+ new(options.merge({:reuse => true}))
28
38
  end
29
39
 
30
40
  # returns an Excel instance
31
41
  # given a WIN32OLE object representing an Excel instance, or a Hash representing options
32
42
  # @param [Hash] options the options
33
- # @option options [Boolean] :reuse
34
- # @option options [Boolean] :displayalerts
35
- # @option options [Boolean] :visible
43
+ # @option options [Boolean] :reuse
44
+ # @option options [Boolean] :visible
45
+ # @option options [Variant] :displayalerts
36
46
  # options:
37
47
  # :reuse connects to an already running Excel instance (true) or
38
- # creates a new Excel instance (false) (default: true)
39
- # :displayalerts allows display alerts in Excel (default: false)
40
- # :visible makes the Excel visible (default: false)
41
- # if :reuse => true, then DisplayAlerts and Visible are set only if they are given
48
+ # creates a new Excel instance (false) (default: true)
49
+ # :visible makes the Excel visible (default: false)
50
+ # :displayalerts enables or disables DisplayAlerts (true, false, :if_visible (default))
42
51
  # @return [Excel] an Excel instance
43
52
  def self.new(options = {})
44
53
  if options.is_a? WIN32OLE
45
- excel = options
54
+ ole_xl = options
46
55
  else
47
56
  options = {:reuse => true}.merge(options)
48
57
  if options[:reuse] == true then
49
- excel = current_excel
58
+ ole_xl = current_excel
50
59
  end
51
60
  end
52
- if not (excel)
53
- excel = WIN32OLE.new('Excel.Application')
61
+ #puts "options: #{options}"
62
+ if not (ole_xl)
63
+ ole_xl = WIN32OLE.new('Excel.Application')
54
64
  options = {
55
- :displayalerts => false,
65
+ :displayalerts => :if_visible,
56
66
  :visible => false,
57
67
  }.merge(options)
58
68
  end
59
- unless options.is_a? WIN32OLE
60
- excel.DisplayAlerts = options[:displayalerts] unless options[:displayalerts].nil?
61
- excel.Visible = options[:visible] unless options[:visible].nil?
62
- end
63
-
64
- hwnd = excel.HWnd
69
+ hwnd = ole_xl.HWnd
65
70
  stored = hwnd2excel(hwnd)
66
-
67
71
  if stored
68
72
  result = stored
69
73
  else
70
74
  result = super(options)
71
- result.instance_variable_set(:@ole_excel, excel)
72
- WIN32OLE.const_load(excel, RobustExcelOle) unless RobustExcelOle.const_defined?(:CONSTANTS)
75
+ result.instance_variable_set(:@ole_excel, ole_xl)
76
+ WIN32OLE.const_load(ole_xl, RobustExcelOle) unless RobustExcelOle.const_defined?(:CONSTANTS)
73
77
  @@hwnd2excel[hwnd] = WeakRef.new(result)
74
78
  end
79
+ unless options.is_a? WIN32OLE
80
+ reused = options[:reuse] && (not stored.nil?)
81
+ visible_value = (reused && options[:visible].nil?) ? result.visible : options[:visible]
82
+ displayalerts_value = (reused && options[:displayalerts].nil?) ? result.displayalerts : options[:displayalerts]
83
+ ole_xl.Visible = visible_value
84
+ ole_xl.DisplayAlerts = (displayalerts_value == :if_visible) ? visible_value : displayalerts_value
85
+ result.instance_variable_set(:@visible, visible_value)
86
+ result.instance_variable_set(:@displayalerts, displayalerts_value)
87
+ end
75
88
  result
76
89
  end
77
90
 
@@ -85,18 +98,17 @@ module RobustExcelOle
85
98
  # @option opts [Boolean] :displayalerts
86
99
  # @option opts [Boolean] :visible
87
100
  # options: reopen_workbooks (default: false): reopen the workbooks in the Excel instances
88
- # :visible (default: false), :displayalerts (default: false)
101
+ # :visible (default: false), :displayalerts (default: :if_visible)
89
102
  # @return [Excel] an Excel instance
90
103
  def recreate(opts = {})
91
104
  unless self.alive?
92
105
  opts = {
93
- :displayalerts => @displayalerts ? @displayalerts : false,
94
- :visible => @visible ? @visible : false
106
+ :visible => @visible ? @visible : false,
107
+ :displayalerts => @displayalerts ? @displayalerts : :if_visible
95
108
  }.merge(opts)
96
- new_excel = WIN32OLE.new('Excel.Application')
97
- new_excel.DisplayAlerts = opts[:displayalerts]
98
- new_excel.Visible = opts[:visible]
99
- @ole_excel = new_excel
109
+ @ole_excel = WIN32OLE.new('Excel.Application')
110
+ self.visible = opts[:visible]
111
+ self.displayalerts = opts[:displayalerts]
100
112
  if opts[:reopen_workbooks]
101
113
  books = book_class.books
102
114
  books.each do |book|
@@ -109,9 +121,9 @@ module RobustExcelOle
109
121
 
110
122
  private
111
123
 
112
- # returns (connects to) a running Excel instance
124
+ # returns a Win32OLE object that represents a Excel instance to which Excel connects
113
125
  # connects to the first opened Excel instance
114
- # if this Excel instance is being closed, then Excel creates a new Excel instance that has the same Hwnd
126
+ # if this Excel instance is being closed, then Excel creates a new Excel instance
115
127
  def self.current_excel # :nodoc: #
116
128
  result = WIN32OLE.connect('Excel.Application') rescue nil
117
129
  if result
@@ -127,9 +139,27 @@ module RobustExcelOle
127
139
 
128
140
  public
129
141
 
142
+ # closes the Excel
143
+ # @param [Hash] options the options
144
+ # @option options [Symbol] :if_unsaved :raise, :save, :forget, or :keep_open
145
+ # @option options [Boolean] :hard
146
+ # :if_unsaved if unsaved workbooks are open in an Excel instance
147
+ # :raise (default) -> raises an exception
148
+ # :save -> saves the workbooks before closing
149
+ # :forget -> closes the Excel instance without saving the workbooks
150
+ # :keep_open -> keeps the Excel instance open
151
+ # :hard kill the Excel instance hard (default: false)
152
+ def close(options = {})
153
+ options = {
154
+ :if_unsaved => :raise,
155
+ :hard => false
156
+ }.merge(options)
157
+ close_excel(options) if managed_unsaved_workbooks(options)
158
+ end
159
+
130
160
  # closes all Excel instances
131
161
  # @param [Hash] options the options
132
- # @option options [Symbol] :if_unsaved :raise, :save, :forget, :alert, or :keep_open
162
+ # @option options [Symbol] :if_unsaved :raise, :save, :forget, or :alert
133
163
  # @option options [Boolean] :hard
134
164
  # @option options [Boolean] :kill_if_timeout
135
165
  # options:
@@ -137,10 +167,9 @@ module RobustExcelOle
137
167
  # :raise (default) -> raises an exception
138
168
  # :save -> saves the workbooks before closing
139
169
  # :forget -> closes the excel instance without saving the workbooks
140
- # :keep_open -> let the workbooks open
141
170
  # :alert -> give control to Excel
142
171
  # :hard closes Excel instances soft (default: false), or, additionally kills the Excel processes hard (true)
143
- # :kill_if_timeout: kills Excel instances hard if the closing process exceeds a certain time limit (default: true)
172
+ # :kill_if_timeout: kills Excel instances hard if the closing process exceeds a certain time limit (default: false)
144
173
  # @raise ExcelError if time limit has exceeded, some Excel instance cannot be closed, or
145
174
  # unsaved workbooks exist and option :if_unsaved is :raise
146
175
  def self.close_all(options={})
@@ -148,21 +177,19 @@ module RobustExcelOle
148
177
  :if_unsaved => :raise,
149
178
  :hard => false,
150
179
  :kill_if_timeout => false
151
- }.merge(options)
152
- excels_number = excel_processes.size
180
+ }.merge(options)
153
181
  timeout = false
182
+ number = excels_number
154
183
  begin
155
- status = Timeout::timeout(5) {
156
- while current_excel do
157
- close_one_excel(options)
158
- GC.start
159
- sleep 0.3
160
- current_excels_number = excel_processes.size
161
- if current_excels_number == excels_number && excels_number > 0
162
- raise ExcelError, "some Excel instance cannot be closed"
184
+ status = Timeout::timeout(15) {
185
+ while (excels_number > 0) do
186
+ ole_xl = current_excel
187
+ begin
188
+ (Excel.new(ole_xl).close(options); Excel.new(ole_xl).close(options)) if ole_xl # two times necessary ?!
189
+ rescue RuntimeError => msg
190
+ raise msg unless msg.message =~ /failed to get Dispatch Interface/
163
191
  end
164
- excels_number = current_excels_number
165
- end
192
+ end
166
193
  }
167
194
  rescue Timeout::Error
168
195
  raise ExcelError, "close_all: timeout" unless options[:kill_if_timeout]
@@ -170,18 +197,52 @@ module RobustExcelOle
170
197
  end
171
198
  kill_all if options[:hard] || (timeout && options[:kill_if_timeout])
172
199
  init
200
+ number
173
201
  end
174
202
 
175
- def self.init
176
- @@hwnd2excel = {}
203
+ def close_excel(options) # :nodoc:
204
+ ole_xl = @ole_excel
205
+ begin
206
+ if options[:if_unsaved] == :alert
207
+ with_displayalerts(true) {ole_xl.Workbooks.Close}
208
+ else
209
+ ole_xl.Workbooks.Close
210
+ end
211
+ rescue WIN32OLERuntimeError => msg
212
+ raise ExcelUserCanceled, "close: canceled by user" if msg.message =~ /80020009/ &&
213
+ options[:if_unsaved] == :alert && (not unsaved_workbooks.empty?)
214
+ end
215
+ excel_hwnd = ole_xl.HWnd
216
+ ole_xl.Quit
217
+ weak_excel_ref = WeakRef.new(ole_xl)
218
+ ole_xl = @ole_excel = nil
219
+ GC.start
220
+ sleep 0.2
221
+ if weak_excel_ref.weakref_alive? then
222
+ begin
223
+ weak_excel_ref.ole_free
224
+ #trace "successfully ole_freed #{weak_excel_ref}"
225
+ rescue => msg
226
+ trace "#{msg.message}"
227
+ trace "could not do ole_free on #{weak_excel_ref}"
228
+ end
229
+ end
230
+ @@hwnd2excel.delete(excel_hwnd)
231
+ if options[:hard] then
232
+ process_id = Win32API.new("user32", "GetWindowThreadProcessId", ["I","P"], "I")
233
+ pid_puffer = " " * 32
234
+ process_id.call(excel_hwnd, pid_puffer)
235
+ pid = pid_puffer.unpack("L")[0]
236
+ Process.kill("KILL", pid) rescue nil
237
+ end
177
238
  end
178
239
 
179
240
  private
180
241
 
181
- def self.manage_unsaved_workbooks(excel, options) # :nodoc: #
182
- unsaved_workbooks = []
242
+ def managed_unsaved_workbooks(options)
243
+ unsaved_workbooks = []
183
244
  begin
184
- excel.Workbooks.each {|w| unsaved_workbooks << w unless (w.Saved || w.ReadOnly)}
245
+ @ole_excel.Workbooks.each {|w| unsaved_workbooks << w unless (w.Saved || w.ReadOnly)}
185
246
  rescue RuntimeError => msg
186
247
  trace "RuntimeError: #{msg.message}"
187
248
  raise ExcelErrorOpen, "Excel instance not alive or damaged" if msg.message =~ /failed to get Dispatch Interface/
@@ -194,77 +255,30 @@ module RobustExcelOle
194
255
  unsaved_workbooks.each do |workbook|
195
256
  workbook.Save
196
257
  end
258
+ return true
197
259
  when :forget
198
260
  # nothing
199
- when :keep_open
200
- return
201
261
  when :alert
202
- begin
203
- excel.DisplayAlerts = true
204
- yield
205
- ensure
206
- begin
207
- excel.DisplayAlerts = false
208
- rescue RuntimeError => msg
209
- trace "RuntimeError: #{msg.message}" if msg.message =~ /failed to get Dispatch Interface/
210
- end
211
- end
212
- return
262
+ # nothing
263
+ when :keep_open
264
+ return false
213
265
  else
214
266
  raise ExcelErrorClose, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}"
215
267
  end
216
268
  end
217
- yield
218
- end
219
-
220
- # closes one Excel instance to which one was connected
221
- def self.close_one_excel(options={})
222
- excel = current_excel
223
- return unless excel
224
- manage_unsaved_workbooks(excel, options) do
225
- weak_ole_excel = WeakRef.new(excel)
226
- excel = nil
227
- close_excel_ole_instance(weak_ole_excel.__getobj__)
228
- end
229
- end
230
-
231
- def self.close_excel_ole_instance(ole_excel) # :nodoc: #
232
- @@hwnd2excel.delete(ole_excel.Hwnd)
233
- excel = ole_excel
234
- ole_excel = nil
235
- begin
236
- excel.Workbooks.Close
237
- excel_hwnd = excel.HWnd
238
- excel.Quit
239
- weak_excel_ref = WeakRef.new(excel)
240
- excel = nil
241
- GC.start
242
- sleep 0.2
243
- if weak_excel_ref.weakref_alive? then
244
- begin
245
- weak_excel_ref.ole_free
246
- trace "successfully ole_freed #{weak_excel_ref}"
247
- rescue
248
- trace "could not do ole_free on #{weak_excel_ref}"
249
- end
250
- end
251
- @@hwnd2excel.delete(excel_hwnd)
252
- rescue => e
253
- trace "Error when closing Excel: #{e.message}"
254
- #t e.backtrace
255
- end
256
- free_all_ole_objects
269
+ return true
257
270
  end
258
271
 
259
272
  # frees all OLE objects in the object space
260
273
  def self.free_all_ole_objects # :nodoc: #
261
274
  anz_objekte = 0
262
- ObjectSpace.each_object(WIN32OLE) do |o|
275
+ ObjectSpace.each_object(WIN32OLE) do |o|
263
276
  anz_objekte += 1
264
- #t [:Name, (o.Name rescue (o.Count rescue "no_name"))]
265
- #t [:ole_object_name, (o.ole_object_name rescue nil)]
266
- #t [:methods, (o.ole_methods rescue nil)] unless (o.Name rescue false)
267
- #t o.ole_type rescue nil
277
+ #trace "#{anz_objekte} name: #{(o.Name rescue (o.Count rescue "no_name"))} ole_object_name: #{(o.ole_object_name rescue nil)} type: #{o.ole_type rescue nil}"
278
+ #trace [:Name, (o.Name rescue (o.Count rescue "no_name"))]
279
+ #trace [:ole_object_name, (o.ole_object_name rescue nil)]
280
+ #trace [:methods, (o.ole_methods rescue nil)] unless (o.Name rescue false)
281
+ #trace o.ole_type rescue nil
268
282
  begin
269
283
  o.ole_free
270
284
  #trace "olefree OK"
@@ -276,62 +290,10 @@ module RobustExcelOle
276
290
  trace "went through #{anz_objekte} OLE objects"
277
291
  end
278
292
 
279
- public
280
293
 
281
- # closes the Excel
282
- # @param [Hash] options the options
283
- # @option options [Symbol] :if_unsaved :raise, :save, :forget, or :keep_open
284
- # @option options [Boolean] :hard
285
- # :if_unsaved if unsaved workbooks are open in an Excel instance
286
- # :raise (default) -> raises an exception
287
- # :save -> saves the workbooks before closing
288
- # :forget -> closes the Excel instance without saving the workbooks
289
- # :keep_open -> keeps the Excel instance open
290
- # :hard kill the Excel instance hard (default: false)
291
- def close(options = {})
292
- options = {
293
- :if_unsaved => :raise,
294
- :hard => false
295
- }.merge(options)
296
- self.class.manage_unsaved_workbooks(@ole_excel, options) do
297
- close_excel(options)
298
- end
299
- end
300
-
301
- private
302
-
303
- def close_excel(options)
304
- excel = @ole_excel
305
- begin
306
- excel.Workbooks.Close
307
- rescue WIN32OLERuntimeError => msg
308
- raise ExcelUserCanceled, "close: canceled by user" if msg.message =~ /80020009/ &&
309
- options[:if_unsaved] == :alert && (not self.unsaved_workbooks.empty?)
310
- end
311
- excel_hwnd = excel.HWnd
312
- excel.Quit
313
- weak_excel_ref = WeakRef.new(excel)
314
- excel = nil
315
- GC.start
316
- sleep 0.2
317
- if weak_excel_ref.weakref_alive? then
318
- begin
319
- weak_excel_ref.ole_free
320
- trace "successfully ole_freed #{weak_excel_ref}"
321
- rescue => msg
322
- trace "#{msg.message}"
323
- trace "could not do ole_free on #{weak_excel_ref}"
324
- end
325
- end
326
- @@hwnd2excel.delete(excel_hwnd)
327
- if options[:hard] then
328
- process_id = Win32API.new("user32", "GetWindowThreadProcessId", ["I","P"], "I")
329
- pid_puffer = " " * 32
330
- process_id.call(excel_hwnd, pid_puffer)
331
- pid = pid_puffer.unpack("L")[0]
332
- Process.kill("KILL", pid) rescue nil
333
- end
334
- end
294
+ def self.init
295
+ @@hwnd2excel = {}
296
+ end
335
297
 
336
298
  public
337
299
 
@@ -342,12 +304,21 @@ module RobustExcelOle
342
304
  processes = procs.InstancesOf("win32_process")
343
305
  number = processes.select{|p| (p.name == "EXCEL.EXE")}.size
344
306
  procs.InstancesOf("win32_process").each do |p|
345
- Process.kill('KILL', p.processid) if p.name == "EXCEL.EXE"
307
+ begin
308
+ Process.kill('KILL', p.processid) if p.name == "EXCEL.EXE"
309
+ rescue
310
+ trace "kill error: #{$!}"
311
+ end
346
312
  end
347
313
  init
348
314
  number
349
315
  end
350
316
 
317
+ def self.excels_number
318
+ WIN32OLE.connect("winmgmts:\\\\.").InstancesOf("win32_process").select{|p| (p.name == "EXCEL.EXE")}.size
319
+ end
320
+
321
+ =begin
351
322
  # provide Excel objects
352
323
  # (so far restricted to all Excel instances opened with RobustExcelOle,
353
324
  # not for Excel instances opened by the user)
@@ -376,6 +347,7 @@ module RobustExcelOle
376
347
  end
377
348
  result
378
349
  end
350
+ =end
379
351
 
380
352
  def excel # :nodoc: #
381
353
  self
@@ -419,7 +391,7 @@ module RobustExcelOle
419
391
  @ole_excel.Name
420
392
  true
421
393
  rescue
422
- #t $!.message
394
+ #trace $!.message
423
395
  false
424
396
  end
425
397
 
@@ -472,34 +444,39 @@ module RobustExcelOle
472
444
 
473
445
  # sets DisplayAlerts in a block
474
446
  def with_displayalerts displayalerts_value
475
- old_displayalerts = @ole_excel.DisplayAlerts
476
- @ole_excel.DisplayAlerts = displayalerts_value
447
+ old_displayalerts = self.displayalerts
448
+ self.displayalerts = displayalerts_value
477
449
  begin
478
450
  yield self
479
451
  ensure
480
- @ole_excel.DisplayAlerts = old_displayalerts if alive?
452
+ self.displayalerts = old_displayalerts if alive?
481
453
  end
482
454
  end
483
455
 
484
456
  # enables DisplayAlerts in the current Excel instance
485
457
  def displayalerts= displayalerts_value
486
- @displayalerts = @ole_excel.DisplayAlerts = displayalerts_value
487
- end
488
-
489
- # return whether DisplayAlerts is enabled in the current Excel instance
490
- def displayalerts
491
- @displayalerts = @ole_excel.DisplayAlerts
458
+ @displayalerts = displayalerts_value
459
+ @ole_excel.DisplayAlerts = (@displayalerts == :if_visible) ? @ole_excel.Visible : displayalerts_value
492
460
  end
493
461
 
494
462
  # makes the current Excel instance visible or invisible
495
463
  def visible= visible_value
496
- @visible = @ole_excel.Visible = visible_value
497
- end
464
+ @ole_excel.Visible = @visible = visible_value
465
+ @ole_excel.DisplayAlerts = @visible if @displayalerts == :if_visible
466
+ end
498
467
 
499
- # returns whether the current Excel instance is visible
500
- def visible
501
- @visible = @ole_excel.Visible
502
- end
468
+ # make all workbooks visible or invisible
469
+ def workbooks_visible= visible_value
470
+ begin
471
+ @ole_excel.Workbooks.each do |ole_wb|
472
+ workbook = Book.new(ole_wb)
473
+ workbook.visible = visible_value
474
+ end
475
+ rescue RuntimeError => msg
476
+ trace "RuntimeError: #{msg.message}"
477
+ raise ExcelErrorOpen, "Excel instance not alive or damaged" if msg.message =~ /failed to get Dispatch Interface/
478
+ end
479
+ end
503
480
 
504
481
  # sets calculation mode in a block
505
482
  def with_calculation(calculation_mode = :automatic)