robust_excel_ole 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog CHANGED
@@ -1,7 +1,7 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- ## [0.3.6] - 2015-10-25
4
+ ## [0.3.6] - 2015-10-27
5
5
 
6
6
  ### Added
7
7
  - Excel#recreate: reopening a closed Excel
@@ -15,7 +15,6 @@ All notable changes to this project will be documented in this file.
15
15
  - Method missing: error messages for dead objects
16
16
  - trace to stdout or file
17
17
 
18
-
19
18
  ### Changed
20
19
 
21
20
  ## [0.3.5] - 2015-08-13
data/README.rdoc CHANGED
@@ -43,6 +43,8 @@ Options are
43
43
  +:default_excel+, +:force_excel+, +:if_absent+, +:if_unsaved+, +:if_obstructed+,
44
44
  +:read_only+, +:visible+, +:displayalerts+.
45
45
 
46
+ Values for +:default_excel+ are +:reuse+, +:new+ or some Excel instance. Values for +:force_excel+ are +:new+ or some Excel instance. Values for +:if_unsaved+ are +:raise+, +:accept+, +:forget+, +:alert+ and +:new_excel+. Values for +:if_obstructed+ are +:raise+, +:save+, +:close_if_saved+, +:forget+, +:alert+ and +:new_excel+. Values for +:if_absent+ are +:raise+ and +:create+.
47
+
46
48
  Here are a few examples:
47
49
 
48
50
  Opening a workbook in the Excel instance where it was opened before, or opening the workbook in a new Excel instance if it was not opened before.
@@ -62,11 +64,13 @@ If a workbook is open and a workbook with the same name but in different path sh
62
64
 
63
65
  book = Book.open('path/workbook.xls', :if_obstructed => :forget)
64
66
 
67
+ Opening linked workbooks for EXCEL 2007 is supported
68
+
65
69
  === Closing a workbook.
66
70
 
67
71
  book.close
68
72
 
69
- There is one option: : +:if_unsaved+. Example:
73
+ There is one option: : +:if_unsaved+. Values for this option are +:raise+, +:save+, +:forget+, +:alert+ and +:keep_open+. Example:
70
74
 
71
75
  Closing the workbook and saving it before if it has unsaved changes.
72
76
 
@@ -89,7 +93,7 @@ An Excel file (or workbook) is represented by a Book object. A Book object is de
89
93
  Identity transparence means that the same Book objects refer to the same Excel files, and vice versa.
90
94
  In other words, a Book objects is a proxy of an Excel file.
91
95
 
92
- === Uplifting a workbook to a Book object
96
+ === Lifting a workbook to a Book object
93
97
 
94
98
  A Book object can be created when giving an Excel workbook.
95
99
 
@@ -324,7 +328,7 @@ Reusing a running Excel instance, making it visible and turning on displayalerts
324
328
 
325
329
  excel2 = Excel.new(:reuse => true, :visible => true, :displayalerts => true).
326
330
 
327
- Uplifting an Excel instance represented as WIN32OLE object to an Excel object
331
+ Lifting an Excel instance represented as WIN32OLE object to an Excel object
328
332
 
329
333
  excel = Excel.new(:reuse => win32ole_object)
330
334
 
@@ -362,17 +366,21 @@ Turning on and off in a block.
362
366
  excel = Excel.current
363
367
  excel.close
364
368
 
365
- Closing the Excel instance and terminating the Excel process
369
+ The options are +:if_unsaved+ and +:hard+ . Example:
366
370
 
367
- excel.close(:hard => true)
371
+ Closing the Excel instance, saving unsaved wrkbooks and terminating the Excel process
372
+
373
+ excel.close(:if_unsaved => :save, :hard => true)
368
374
 
369
375
  === Closing all Excel instances.
370
376
 
371
377
  Excel.close_all
372
378
 
373
- Closing the Excel instances and terminating the Excel processes
379
+ The options are +:if_unsaved+ and +:hard+ . Values for :if_unsaved+ are +raise+, +save+, +forget+. Example:
380
+
381
+ Closing all Excel instances, not saving unsaved workbooks and terminating the Excel processes
374
382
 
375
- Excel.close_all(:hard => true)
383
+ Excel.close_all(:if_unsaved => :forget, :hard => :true)
376
384
 
377
385
  === Terminating all Excel processes
378
386
 
@@ -385,9 +393,9 @@ Reopening the closed Excel instance. This includes reopening all workbooks that
385
393
  excel.close
386
394
  excel.recreate
387
395
 
388
- Options are :visible and :displayalerts
396
+ The options are :reopen_workbooks, :visible and :displayalerts.
389
397
 
390
- excel.recreate(:visible => true, :displayalerts => true)
398
+ excel.recreate(:reopen_workbooks => true, :visible => true, :displayalerts => true)
391
399
 
392
400
  === Providing Excel instances
393
401
 
@@ -28,7 +28,7 @@ module RobustExcelOle
28
28
  else
29
29
  if REO_LOG_DIR.empty?
30
30
  homes = ["HOME", "HOMEPATH"]
31
- home = homes.detect {|h| ENV[h] != nil}
31
+ home = homes.find {|h| ENV[h] != nil}
32
32
  reo_log_dir = ENV[home]
33
33
  else
34
34
  reo_log_dir = REO_LOG_DIR
@@ -69,7 +69,9 @@ class Object # :nodoc: #
69
69
  def excel
70
70
  raise ExcelError, "receiver instance is neither an Excel nor a Book"
71
71
  end
72
+ end
72
73
 
74
+ class WIN32OLE
73
75
  end
74
76
 
75
77
  class ::String # :nodoc: #
@@ -5,6 +5,7 @@ require 'weakref'
5
5
  module RobustExcelOle
6
6
 
7
7
  class Book
8
+
8
9
  attr_accessor :excel
9
10
  attr_accessor :workbook
10
11
  attr_accessor :stored_filename
@@ -67,9 +68,9 @@ module RobustExcelOle
67
68
  # if readonly is true, then prefer a book that is given in force_excel if this option is set
68
69
  book = bookstore.fetch(file,
69
70
  :prefer_writable => (not options[:read_only]),
70
- :prefer_excel => (options[:read_only] ? options[:force_excel].excel : nil)) rescue nil
71
+ :prefer_excel => (options[:read_only] ? excel_of(options[:force_excel]) : nil)) rescue nil
71
72
  if book
72
- if (((not options[:force_excel]) || (options[:force_excel].excel == book.excel)) &&
73
+ if (((not options[:force_excel]) || (excel_of(options[:force_excel]) == book.excel)) &&
73
74
  (not (book.alive? && (not book.saved) && (not options[:if_unsaved] == :accept))))
74
75
  book.options = DEFAULT_OPEN_OPTS.merge(opts)
75
76
  book.ensure_excel(options) unless book.excel.alive?
@@ -81,18 +82,23 @@ module RobustExcelOle
81
82
  end
82
83
  end
83
84
  end
84
- #options[:excel] = options[:force_excel] ? options[:force_excel] : options[:default_excel]
85
85
  new(file, options, &block)
86
86
  end
87
87
  end
88
88
 
89
89
  # creates a Book object for a given workbook or file name
90
+ # @param [WIN32OLE] workbook a workbook
91
+ # @options opts [Symbol] see above
92
+ # @return [Book] a Book object
90
93
  def self.new(workbook, opts={ }, &block)
91
- if workbook && workbook.class == WIN32OLE
94
+ if workbook && (workbook.is_a? WIN32OLE)
92
95
  filename = workbook.Fullname.tr('\\','/') rescue nil
93
96
  if filename
94
97
  book = bookstore.fetch(filename)
95
- return book if book && book.alive?
98
+ if book && book.alive?
99
+ book.apply_options(opts)
100
+ return book
101
+ end
96
102
  end
97
103
  end
98
104
  super
@@ -103,16 +109,15 @@ module RobustExcelOle
103
109
  def initialize(file_or_workbook, opts={ }, &block)
104
110
  options = DEFAULT_OPEN_OPTS.merge(opts)
105
111
  options[:excel] = options[:force_excel] ? options[:force_excel] : options[:default_excel]
106
- if file_or_workbook.class == WIN32OLE
112
+ if file_or_workbook.is_a? WIN32OLE
107
113
  workbook = file_or_workbook
108
114
  @workbook = workbook
109
115
  # use the Excel instance where the workbook is opened
110
116
  win32ole_excel = WIN32OLE.connect(workbook.Fullname).Application rescue nil
111
- options = {:reuse => win32ole_excel}.merge(options)
112
- @excel = Excel.new(options)
117
+ @excel = excel_class.new(win32ole_excel)
118
+ self.apply_options(options)
113
119
  # if the Excel could not be lifted up, then create it
114
- ensure_excel(options) unless (@excel && @excel.alive?)
115
- t "@excel: #{@excel}"
120
+ ensure_excel(options)
116
121
  else
117
122
  file = file_or_workbook
118
123
  ensure_excel(options)
@@ -128,7 +133,35 @@ module RobustExcelOle
128
133
  end
129
134
  end
130
135
 
136
+ def apply_options(options) # :nodoc: #
137
+ @excel.visible = options[:visible] unless options[:visible].nil?
138
+ @excel.displayalerts = options[:displayalerts] unless options[:displayalerts].nil?
139
+ end
140
+
141
+ private
142
+
143
+ # returns an Excel object when given Excel, Book or Win32ole object representing a Workbook or an Excel
144
+ def self.excel_of(object)
145
+ if object.is_a? WIN32OLE
146
+ case object.ole_obj_help.name
147
+ when /Workbook/i
148
+ new(object).excel
149
+ when /Application/i
150
+ excel_class.new(object)
151
+ else
152
+ object.excel
153
+ end
154
+ else
155
+ object.excel
156
+ end
157
+ #rescue
158
+ # t "no Excel, Book, or WIN32OLE object representing a Workbook or an Excel instance"
159
+ end
160
+
161
+ public
162
+
131
163
  def ensure_excel(options)
164
+ return if @excel && @excel.alive?
132
165
  if options[:excel] == :reuse
133
166
  @excel = excel_class.new(:reuse => true)
134
167
  end
@@ -146,7 +179,7 @@ module RobustExcelOle
146
179
  excel_options[:reuse] = false
147
180
  @excel = excel_class.new(excel_options)
148
181
  else
149
- @excel = options[:excel].excel
182
+ @excel = self.class.excel_of(options[:excel])
150
183
  end
151
184
  end
152
185
  # if :excel => :new or (:excel => :reuse but could not reuse)
@@ -251,7 +284,13 @@ module RobustExcelOle
251
284
  t "WeakRefError: #{msg.message}"
252
285
  raise ExcelErrorOpen, "#{msg.message}"
253
286
  end
287
+ # workaround for linked workbooks for Excel 2007:
288
+ # opening and closing a dummy workbook if Excel has no workbooks.
289
+ # delay: with visible: 0.2 sec, without visible almost none
290
+ count = workbooks.Count
291
+ workbooks.Add if @excel.Version == "12.0" && count == 0
254
292
  workbooks.Open(filename,{ 'ReadOnly' => options[:read_only] })
293
+ workbooks.Item(1).Close if @excel.Version == "12.0" && count == 0
255
294
  rescue WIN32OLERuntimeError => msg
256
295
  t "WIN32OLERuntimeError: #{msg.message}"
257
296
  if msg.message =~ /800A03EC/
@@ -279,6 +318,7 @@ module RobustExcelOle
279
318
  # :raise (default) -> raises an exception
280
319
  # :save -> saves the workbook before it is closed
281
320
  # :forget -> closes the workbook
321
+ # :keep_open -> keep the workbook open
282
322
  # :alert -> gives control to excel
283
323
  def close(opts = {:if_unsaved => :raise})
284
324
  if (alive? && (not @workbook.Saved) && writable) then
@@ -290,6 +330,8 @@ module RobustExcelOle
290
330
  close_workbook
291
331
  when :forget
292
332
  close_workbook
333
+ when :keep_open
334
+ # nothing
293
335
  when :alert
294
336
  @excel.with_displayalerts true do
295
337
  close_workbook
@@ -440,7 +482,9 @@ module RobustExcelOle
440
482
  value
441
483
  end
442
484
 
443
- # set the contents of a range with given name
485
+ # sets the contents of a range with given name
486
+ # @param [String] name the range name
487
+ # @param [Variant] value the contents of the range
444
488
  def set_nvalue(name, value)
445
489
  begin
446
490
  item = self.Names.Item(name)
@@ -514,7 +558,7 @@ module RobustExcelOle
514
558
  end
515
559
 
516
560
  # simple save of a workbook.
517
- # returns true, if successfully saved, nil or error otherwise
561
+ # @return [Boolean] true, if successfully saved, nil or error otherwise
518
562
  def save
519
563
  raise ExcelErrorSave, "Workbook is not alive" if (not alive?)
520
564
  raise ExcelErrorSave, "Not opened for writing (opened with :read_only option)" if @workbook.ReadOnly
@@ -531,6 +575,8 @@ module RobustExcelOle
531
575
  end
532
576
 
533
577
  # saves a workbook with a given file name.
578
+ # @param [String] file the file name
579
+ # @option opts [Symbol] :if_exists if a file with the same name exists, then
534
580
  #
535
581
  # options:
536
582
  # :if_exists if a file with the same name exists, then
@@ -630,7 +676,7 @@ module RobustExcelOle
630
676
 
631
677
  public
632
678
 
633
- # returns a sheet, if a name of a sheet or a number is given
679
+ # returns a sheet, if a sheet name or a number is given
634
680
  # returns the value of the range, if a global name of a range in the book is given
635
681
  def [] name
636
682
  name += 1 if name.is_a? Numeric
@@ -646,6 +692,8 @@ module RobustExcelOle
646
692
  end
647
693
 
648
694
  # sets the value of a range given its name
695
+ # @param [String] name the name of the range
696
+ # @param [Variant] value the contents of the range
649
697
  def []= (name, value)
650
698
  set_nvalue(name,value)
651
699
  end
@@ -656,6 +704,11 @@ module RobustExcelOle
656
704
  end
657
705
  end
658
706
 
707
+ # adds a sheet to the workbook
708
+ # @param [Sheet] sheet a sheet
709
+ # @option opts [Symbol] :as new name of the copyed sheet
710
+ # @option opts [Symbol] :before a sheet before which the sheet shall be inserted
711
+ # @option opts [Symbol] :after a sheet after which the sheet shall be inserted
659
712
  def add_sheet(sheet = nil, opts = { })
660
713
  if sheet.is_a? Hash
661
714
  opts = sheet
@@ -700,6 +753,10 @@ module RobustExcelOle
700
753
  "<#Book: " + "#{"not alive " unless alive?}" + "#{File.basename(self.filename) if alive?}" + " #{@workbook} #{@excel}" + ">"
701
754
  end
702
755
 
756
+ def self.in_context(klass)
757
+
758
+ end
759
+
703
760
  def self.excel_class
704
761
  @excel_class ||= begin
705
762
  module_name = self.parent_name
@@ -749,6 +806,8 @@ module RobustExcelOle
749
806
 
750
807
  public
751
808
 
809
+ Workbook = Book
810
+
752
811
  class ExcelError < RuntimeError # :nodoc: #
753
812
  end
754
813
 
@@ -1,5 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ require 'timeout'
4
+
3
5
  module RobustExcelOle
4
6
 
5
7
  class Excel
@@ -18,18 +20,20 @@ module RobustExcelOle
18
20
  end
19
21
 
20
22
  # returns an Excel instance
21
- # options:
22
- # :reuse uses an already running Excel instance (true) or
23
- # uses the Excel instance represented as WIN32OLE object (default: true)
23
+ # given: a WIN32OLE object representing an Excel instance, or a Hash representing options:
24
+ # :reuse connects to an already running Excel instance (true) or
25
+ # creates a new Excel instance (false) (default: true)
24
26
  # :displayalerts allows display alerts in Excel (default: false)
25
27
  # :visible makes the Excel visible (default: false)
26
28
  # if :reuse => true, then DisplayAlerts and Visible are set only if they are given
27
- def self.new(options= {})
28
- options = {:reuse => true}.merge(options)
29
- if options[:reuse] == true then
30
- excel = current_excel
31
- elsif options[:reuse].class == WIN32OLE then
32
- excel = options[:reuse]
29
+ def self.new(options = {})
30
+ if options.is_a? WIN32OLE
31
+ excel = options
32
+ else
33
+ options = {:reuse => true}.merge(options)
34
+ if options[:reuse] == true then
35
+ excel = current_excel
36
+ end
33
37
  end
34
38
  if not (excel)
35
39
  excel = WIN32OLE.new('Excel.Application')
@@ -38,8 +42,10 @@ module RobustExcelOle
38
42
  :visible => false,
39
43
  }.merge(options)
40
44
  end
41
- excel.DisplayAlerts = options[:displayalerts] unless options[:displayalerts].nil?
42
- excel.Visible = options[:visible] unless options[:visible].nil?
45
+ unless options.is_a? WIN32OLE
46
+ excel.DisplayAlerts = options[:displayalerts] unless options[:displayalerts].nil?
47
+ excel.Visible = options[:visible] unless options[:visible].nil?
48
+ end
43
49
 
44
50
  hwnd = excel.HWnd
45
51
  stored = hwnd2excel(hwnd)
@@ -51,7 +57,6 @@ module RobustExcelOle
51
57
  result.instance_variable_set(:@ole_excel, excel)
52
58
  WIN32OLE.const_load(excel, RobustExcelOle) unless RobustExcelOle.const_defined?(:CONSTANTS)
53
59
  @@hwnd2excel[hwnd] = WeakRef.new(result)
54
-
55
60
  end
56
61
  result
57
62
  end
@@ -61,21 +66,24 @@ module RobustExcelOle
61
66
  end
62
67
 
63
68
  # reopens a closed Excel instance
64
- # options: :visible (default: false), :displayalerts (default: false)
69
+ # options: reopen_workbooks (default: false): reopen the workbooks in the Excel instances
70
+ # :visible (default: false), :displayalerts (default: false)
65
71
  def recreate(opts = {})
66
72
  unless self.alive?
67
73
  opts = {
68
- :displayalerts => false,
69
- :visible => false,
74
+ :displayalerts => @displayalerts ? @displayalerts : false,
75
+ :visible => @visible ? @visible : false
70
76
  }.merge(opts)
71
77
  new_excel = WIN32OLE.new('Excel.Application')
72
78
  new_excel.DisplayAlerts = opts[:displayalerts]
73
79
  new_excel.Visible = opts[:visible]
74
80
  @ole_excel = new_excel
75
- books = Book.books
76
- books.each do |book|
77
- book.reopen if ((not book.alive?) && book.excel.alive?)
78
- end
81
+ if opts[:reopen_workbooks]
82
+ books = book_class.books
83
+ books.each do |book|
84
+ book.reopen if ((not book.alive?) && book.excel.alive? && book.excel == self)
85
+ end
86
+ end
79
87
  end
80
88
  self
81
89
  end
@@ -84,7 +92,9 @@ module RobustExcelOle
84
92
 
85
93
  # returns an Excel instance to which one 'connect' was possible
86
94
  def self.current_excel # :nodoc: #
95
+ #p "current_excel:"
87
96
  result = WIN32OLE.connect('Excel.Application') rescue nil
97
+ #p "result: #{result}"
88
98
  if result
89
99
  begin
90
100
  result.Visible # send any method, just to see if it responds
@@ -93,6 +103,7 @@ module RobustExcelOle
93
103
  return nil
94
104
  end
95
105
  end
106
+ #p "result: #{result}"
96
107
  result
97
108
  end
98
109
 
@@ -104,24 +115,36 @@ module RobustExcelOle
104
115
  # :raise (default) -> raises an exception
105
116
  # :save -> saves the workbooks before closing
106
117
  # :forget -> closes the excel instance without saving the workbooks
107
- # :alert -> gives control to Excel
108
- # :hard kills the Excel instances hard (default: false)
118
+ # :alert -> give control to Excel
119
+ # :hard closes Excel instances soft (default: false), or, additionally kills the Excel processes hard (true)
120
+ # :kill_if_timeout: kills Excel instances hard if the closing process exceeds a certain time limit (default: true)
109
121
  def self.close_all(options={})
110
122
  options = {
111
123
  :if_unsaved => :raise,
112
- :hard => false
124
+ :hard => false,
125
+ :kill_if_timeout => false
113
126
  }.merge(options)
114
127
  excels_number = excel_processes.size
115
- # kill hard if the number of Excel instqnces does not decrease
116
- while current_excel do
117
- close_one_excel(options)
118
- GC.start
119
- sleep 0.3
120
- current_excels_number = excel_processes.size
121
- (current_excels_number < excels_number) ? (excels_number = current_excels_number) : kill_all
128
+ timeout = false
129
+ begin
130
+ status = Timeout::timeout(5) {
131
+ while current_excel do
132
+ close_one_excel(options)
133
+ GC.start
134
+ sleep 0.3
135
+ current_excels_number = excel_processes.size
136
+ if current_excels_number == excels_number && excels_number > 0
137
+ raise ExcelError, "some Excel instance cannot be closed"
138
+ end
139
+ excels_number = current_excels_number
140
+ end
141
+ }
142
+ rescue Timeout::Error
143
+ raise ExcelError, "close_all: timeout" unless options[:kill_if_timeout]
144
+ timeout = true
122
145
  end
146
+ kill_all if options[:hard] || (timeout && options[:kill_if_timeout])
123
147
  init
124
- kill_all if options[:hard]
125
148
  end
126
149
 
127
150
  def self.init
@@ -131,7 +154,6 @@ module RobustExcelOle
131
154
  private
132
155
 
133
156
  def self.manage_unsaved_workbooks(excel, options)
134
- #this_excel = excel == 0 ? self : excel
135
157
  unsaved_workbooks = []
136
158
  begin
137
159
  excel.Workbooks.each {|w| unsaved_workbooks << w unless (w.Saved || w.ReadOnly)}
@@ -149,22 +171,32 @@ module RobustExcelOle
149
171
  end
150
172
  when :forget
151
173
  # nothing
174
+ when :keep_open
175
+ return
152
176
  when :alert
153
- excel.DisplayAlerts = true
177
+ begin
178
+ excel.DisplayAlerts = true
179
+ yield
180
+ ensure
181
+ excel.DisplayAlerts = false
182
+ end
183
+ return
154
184
  else
155
185
  raise ExcelErrorClose, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}"
156
186
  end
157
187
  end
188
+ yield
158
189
  end
159
190
 
160
191
  # closes one Excel instance to which one was connected
161
192
  def self.close_one_excel(options={})
162
193
  excel = current_excel
163
194
  return unless excel
164
- manage_unsaved_workbooks(excel, options)
165
- weak_ole_excel = WeakRef.new(excel)
166
- excel = nil
167
- close_excel_ole_instance(weak_ole_excel.__getobj__)
195
+ manage_unsaved_workbooks(excel, options) do
196
+ weak_ole_excel = WeakRef.new(excel)
197
+ excel = nil
198
+ close_excel_ole_instance(weak_ole_excel.__getobj__)
199
+ end
168
200
  end
169
201
 
170
202
  def self.close_excel_ole_instance(ole_excel)
@@ -180,7 +212,6 @@ module RobustExcelOle
180
212
  GC.start
181
213
  sleep 0.2
182
214
  if weak_excel_ref.weakref_alive? then
183
- #if WIN32OLE.ole_reference_count(weak_xlapp) > 0
184
215
  begin
185
216
  weak_excel_ref.ole_free
186
217
  t "successfully ole_freed #{weak_excel_ref}"
@@ -188,7 +219,6 @@ module RobustExcelOle
188
219
  t "could not do ole_free on #{weak_excel_ref}"
189
220
  end
190
221
  end
191
- hwnd2excel(excel_hwnd).die rescue nil
192
222
  @@hwnd2excel.delete(excel_hwnd)
193
223
  rescue => e
194
224
  t "Error when closing Excel: #{e.message}"
@@ -217,11 +247,6 @@ module RobustExcelOle
217
247
  t "went through #{anz_objekte} OLE objects"
218
248
  end
219
249
 
220
- # sets this Excel instance to nil
221
- def die
222
- @ole_excel = nil
223
- end
224
-
225
250
  public
226
251
 
227
252
  # closes the Excel
@@ -229,15 +254,16 @@ module RobustExcelOle
229
254
  # :raise (default) -> raises an exception
230
255
  # :save -> saves the workbooks before closing
231
256
  # :forget -> closes the Excel instance without saving the workbooks
232
- # :alert -> gives control to Excel
257
+ # :keep_open -> keeps the Excel instance open
233
258
  # :hard kill the Excel instance hard (default: false)
234
259
  def close(options = {})
235
260
  options = {
236
261
  :if_unsaved => :raise,
237
262
  :hard => false
238
263
  }.merge(options)
239
- self.class.manage_unsaved_workbooks(@ole_excel, options)
240
- close_excel(options)
264
+ self.class.manage_unsaved_workbooks(@ole_excel, options) do
265
+ close_excel(options)
266
+ end
241
267
  end
242
268
 
243
269
  private
@@ -257,7 +283,6 @@ module RobustExcelOle
257
283
  GC.start
258
284
  sleep 0.2
259
285
  if weak_excel_ref.weakref_alive? then
260
- #if WIN32OLE.ole_reference_count(weak_xlapp) > 0
261
286
  begin
262
287
  weak_excel_ref.ole_free
263
288
  t "successfully ole_freed #{weak_excel_ref}"
@@ -266,7 +291,6 @@ module RobustExcelOle
266
291
  t "could not do ole_free on #{weak_excel_ref}"
267
292
  end
268
293
  end
269
- hwnd2excel(excel_hwnd).die rescue nil
270
294
  @@hwnd2excel.delete(excel_hwnd)
271
295
  if options[:hard] then
272
296
  Excel.free_all_ole_objects
@@ -288,6 +312,7 @@ module RobustExcelOle
288
312
  procs.InstancesOf("win32_process").each do |p|
289
313
  Process.kill('KILL', p.processid) if p.name == "EXCEL.EXE"
290
314
  end
315
+ init
291
316
  number
292
317
  end
293
318
 
@@ -297,11 +322,12 @@ module RobustExcelOle
297
322
  def self.excel_processes
298
323
  pid2excel = {}
299
324
  @@hwnd2excel.each do |hwnd,wr_excel|
325
+ excel = wr_excel.__getobj__
300
326
  process_id = Win32API.new("user32", "GetWindowThreadProcessId", ["I","P"], "I")
301
327
  pid_puffer = " " * 32
302
328
  process_id.call(hwnd, pid_puffer)
303
329
  pid = pid_puffer.unpack("L")[0]
304
- pid2excel[pid] = wr_excel
330
+ pid2excel[pid] = excel
305
331
  end
306
332
  procs = WIN32OLE.connect("winmgmts:\\\\.")
307
333
  processes = procs.InstancesOf("win32_process")
@@ -309,11 +335,11 @@ module RobustExcelOle
309
335
  processes.each do |p|
310
336
  if p.name == "EXCEL.EXE"
311
337
  if pid2excel.include?(p.processid)
312
- excel = pid2excel[p.processid].__getobj__
338
+ excel = pid2excel[p.processid]
313
339
  result << excel
314
340
  end
315
- # how to connect with an Excel instance that is not in hwnd2excel (uplifting Excel)?
316
- #excel = Excel.uplift unless (excel && excel.alive?)
341
+ # how to connect to an (interactively opened) Excel instance and get a WIN32OLE object?
342
+ # after that, lift it to an Excel object
317
343
  end
318
344
  end
319
345
  result
@@ -353,7 +379,7 @@ module RobustExcelOle
353
379
 
354
380
  # returns true, if the Excel instances are alive and identical, false otherwise
355
381
  def == other_excel
356
- self.Hwnd == other_excel.Hwnd if other_excel.is_a?(Excel) && self.alive? && other_excel.alive?
382
+ self.Hwnd == other_excel.Hwnd if other_excel.is_a?(Excel) && self.alive? && other_excel.alive?
357
383
  end
358
384
 
359
385
  # returns true, if the Excel instances responds to VBA methods, false otherwise
@@ -365,11 +391,18 @@ module RobustExcelOle
365
391
  false
366
392
  end
367
393
 
368
- def print_workbooks
369
- self.Workbooks.each {|w| t "#{w.Name} #{w}"}
394
+
395
+ # returns all unsaved workbooks in Excel instances
396
+ def self.unsaved_workbooks_all
397
+ result = []
398
+ @@hwnd2excel.each do |hwnd,wr_excel|
399
+ excel = wr_excel.__getobj__
400
+ result << excel.unsaved_workbooks
401
+ end
402
+ result
370
403
  end
371
404
 
372
- # returns all unsaved workbooks
405
+ # returns unsaved workbooks
373
406
  def unsaved_workbooks
374
407
  result = []
375
408
  begin
@@ -383,6 +416,11 @@ module RobustExcelOle
383
416
  # yields different WIN32OLE objects than book.workbook
384
417
  #self.class.map {|w| (not w.Saved)}
385
418
 
419
+ def print_workbooks
420
+ self.Workbooks.each {|w| t "#{w.Name} #{w}"}
421
+ end
422
+
423
+
386
424
  # empty workbook is generated, saved and closed
387
425
  def generate_workbook file_name
388
426
  self.Workbooks.Add
@@ -416,22 +454,22 @@ module RobustExcelOle
416
454
 
417
455
  # enables DisplayAlerts in the current Excel instance
418
456
  def displayalerts= displayalerts_value
419
- @ole_excel.DisplayAlerts = displayalerts_value
457
+ @displayalerts = @ole_excel.DisplayAlerts = displayalerts_value
420
458
  end
421
459
 
422
460
  # return if in the current Excel instance DisplayAlerts is enabled
423
461
  def displayalerts
424
- @ole_excel.DisplayAlerts
462
+ @displayalerts = @ole_excel.DisplayAlerts
425
463
  end
426
464
 
427
465
  # makes the current Excel instance visible or invisible
428
466
  def visible= visible_value
429
- @ole_excel.Visible = visible_value
467
+ @visible = @ole_excel.Visible = visible_value
430
468
  end
431
469
 
432
470
  # returns whether the current Excel instance is visible
433
471
  def visible
434
- @ole_excel.Visible
472
+ @visible = @ole_excel.Visible
435
473
  end
436
474
 
437
475
  def to_s
@@ -442,6 +480,19 @@ module RobustExcelOle
442
480
  self.to_s
443
481
  end
444
482
 
483
+ def self.book_class
484
+ @book_class ||= begin
485
+ module_name = self.parent_name
486
+ "#{module_name}::Book".constantize
487
+ rescue NameError => e
488
+ book
489
+ end
490
+ end
491
+
492
+ def book_class
493
+ self.class.book_class
494
+ end
495
+
445
496
  private
446
497
 
447
498
  def method_missing(name, *args)