robust_excel_ole 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,18 +6,20 @@ module RobustExcelOle
6
6
  class Bookstore
7
7
 
8
8
  def initialize
9
- @filename2books = Hash.new {|hash, key| hash[key] = [] }
9
+ @filename2books ||= Hash.new {|hash, key| hash[key] = [] }
10
10
  @hidden_excel_instance = nil
11
11
  end
12
12
 
13
13
  # returns a book with the given filename, if it was open once
14
14
  # prefers open books to closed books, and among them, prefers more recently opened books
15
15
  # excludes hidden Excel instance
16
- # options: :prefer_writable return the writable book, if it is open (default: true)
17
- # otherwise return the book according to the preference order mentioned above
18
- # :prefer_excel return the book in the given excel instance, if it exists,
19
- # otherwise proceed according to prefer_writable
16
+ # options: :prefer_writable returns the writable book, if it is open (default: true)
17
+ # otherwise returns the book according to the preference order mentioned above
18
+ # :prefer_excel returns the book in the given Excel instance, if it exists,
19
+ # otherwise proceeds according to prefer_writable
20
20
  def fetch(filename, options = {:prefer_writable => true })
21
+ return nil unless filename
22
+ filename = RobustExcelOle::absolute_path(filename)
21
23
  filename_key = RobustExcelOle::canonize(filename)
22
24
  weakref_books = @filename2books[filename_key]
23
25
  return nil unless weakref_books
@@ -27,7 +29,7 @@ module RobustExcelOle
27
29
  begin
28
30
  @filename2books[filename_key].delete(wr_book)
29
31
  rescue
30
- puts "Warning: deleting dead reference failed! (file: #{filename})"
32
+ t "Warning: deleting dead reference failed: file: #{filename.inspect}"
31
33
  end
32
34
  else
33
35
  book = wr_book.__getobj__
@@ -48,7 +50,7 @@ module RobustExcelOle
48
50
  result if result
49
51
  end
50
52
 
51
- # stores a book
53
+ # stores a workbook
52
54
  def store(book)
53
55
  filename_key = RobustExcelOle::canonize(book.filename)
54
56
  if book.stored_filename
@@ -60,7 +62,7 @@ module RobustExcelOle
60
62
  book.stored_filename = book.filename
61
63
  end
62
64
 
63
- # creates and returns a separate Excel instance with Visible and DisplayAlerts false
65
+ # creates and returns a separate Excel instance with Visible and DisplayAlerts equal false
64
66
  def hidden_excel
65
67
  unless (@hidden_excel_instance && @hidden_excel_instance.weakref_alive? && @hidden_excel_instance.__getobj__.alive?)
66
68
  @hidden_excel_instance = WeakRef.new(Excel.create)
@@ -68,6 +70,21 @@ module RobustExcelOle
68
70
  @hidden_excel_instance.__getobj__
69
71
  end
70
72
 
73
+ # returns all stored books
74
+ def books
75
+ result = []
76
+ if @filename2books
77
+ @filename2books.each do |filename,books|
78
+ unless books.empty?
79
+ books.each do |wr_book|
80
+ result << wr_book.__getobj__ if wr_book.weakref_alive?
81
+ end
82
+ end
83
+ end
84
+ end
85
+ result
86
+ end
87
+
71
88
  private
72
89
 
73
90
  def try_hidden_excel
@@ -76,20 +93,19 @@ module RobustExcelOle
76
93
 
77
94
  # prints the book store
78
95
  def print
79
- p "@filename2books:"
96
+ t "@filename2books:"
80
97
  if @filename2books
81
98
  @filename2books.each do |filename,books|
82
- p " filename: #{filename}"
83
- p " books:"
84
- p " []" if books == []
85
- if books
99
+ t " filename: #{filename}"
100
+ t " books:"
101
+ if books.empty?
102
+ t " []"
103
+ else
86
104
  books.each do |book|
87
105
  if book.weakref_alive?
88
- p "#{book}"
89
- p "excel: #{book.excel}"
90
- p "alive: #{book.alive?}"
106
+ t "#{book}"
91
107
  else
92
- p "weakref not alive"
108
+ t "weakref not alive"
93
109
  end
94
110
  end
95
111
  end
@@ -19,14 +19,17 @@ module RobustExcelOle
19
19
 
20
20
  # returns an Excel instance
21
21
  # options:
22
- # :reuse use an already running Excel instance (default: true)
23
- # :displayalerts allow display alerts in Excel (default: false)
24
- # :visible make visible in Excel (default: false)
22
+ # :reuse uses an already running Excel instance (true) or
23
+ # uses the Excel instance represented as WIN32OLE object (default: true)
24
+ # :displayalerts allows display alerts in Excel (default: false)
25
+ # :visible makes the Excel visible (default: false)
25
26
  # if :reuse => true, then DisplayAlerts and Visible are set only if they are given
26
27
  def self.new(options= {})
27
28
  options = {:reuse => true}.merge(options)
28
- if options[:reuse] then
29
+ if options[:reuse] == true then
29
30
  excel = current_excel
31
+ elsif options[:reuse].class == WIN32OLE then
32
+ excel = options[:reuse]
30
33
  end
31
34
  if not (excel)
32
35
  excel = WIN32OLE.new('Excel.Application')
@@ -45,9 +48,10 @@ module RobustExcelOle
45
48
  result = stored
46
49
  else
47
50
  result = super(options)
48
- result.instance_variable_set(:@this_excel, excel)
51
+ result.instance_variable_set(:@ole_excel, excel)
49
52
  WIN32OLE.const_load(excel, RobustExcelOle) unless RobustExcelOle.const_defined?(:CONSTANTS)
50
- @@hwnd2excel[hwnd] = result
53
+ @@hwnd2excel[hwnd] = WeakRef.new(result)
54
+
51
55
  end
52
56
  result
53
57
  end
@@ -56,42 +60,86 @@ module RobustExcelOle
56
60
  @excel = self
57
61
  end
58
62
 
63
+ # reopens a closed Excel instance
64
+ # options: :visible (default: false), :displayalerts (default: false)
65
+ def recreate(opts = {})
66
+ unless self.alive?
67
+ opts = {
68
+ :displayalerts => false,
69
+ :visible => false,
70
+ }.merge(opts)
71
+ new_excel = WIN32OLE.new('Excel.Application')
72
+ new_excel.DisplayAlerts = opts[:displayalerts]
73
+ new_excel.Visible = opts[:visible]
74
+ @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
79
+ end
80
+ self
81
+ end
82
+
83
+ private
84
+
85
+ # returns an Excel instance to which one 'connect' was possible
86
+ def self.current_excel # :nodoc: #
87
+ result = WIN32OLE.connect('Excel.Application') rescue nil
88
+ if result
89
+ begin
90
+ result.Visible # send any method, just to see if it responds
91
+ rescue
92
+ t "dead excel " + ("Window-handle = #{result.HWnd}" rescue "without window handle")
93
+ return nil
94
+ end
95
+ end
96
+ result
97
+ end
98
+
99
+ public
100
+
59
101
  # closes all Excel instances
60
102
  # options:
61
103
  # :if_unsaved if unsaved workbooks are open in an Excel instance
62
- # :raise (default) -> raise an exception
63
- # :save -> save the workbooks before closing
64
- # :forget -> close the excel instance without saving the workbooks
65
- # :alert -> give control to Excel
66
- # :hard kill the Excel instances hard (default: false)
104
+ # :raise (default) -> raises an exception
105
+ # :save -> saves the workbooks before closing
106
+ # :forget -> closes the excel instance without saving the workbooks
107
+ # :alert -> gives control to Excel
108
+ # :hard kills the Excel instances hard (default: false)
67
109
  def self.close_all(options={})
68
110
  options = {
69
111
  :if_unsaved => :raise,
70
112
  :hard => false
71
- }.merge(options)
113
+ }.merge(options)
114
+ excels_number = excel_processes.size
115
+ # kill hard if the number of Excel instqnces does not decrease
72
116
  while current_excel do
73
- #current_excel.close(options)
74
- close_one_excel
117
+ close_one_excel(options)
75
118
  GC.start
76
119
  sleep 0.3
77
- # free_all_ole_objects if options[:hard] ???
120
+ current_excels_number = excel_processes.size
121
+ (current_excels_number < excels_number) ? (excels_number = current_excels_number) : kill_all
78
122
  end
123
+ init
124
+ kill_all if options[:hard]
79
125
  end
80
126
 
81
- # close the Excel
82
- # :if_unsaved if unsaved workbooks are open in an Excel instance
83
- # :raise (default) -> raise an exception
84
- # :save -> save the workbooks before closing
85
- # :forget -> close the excel instance without saving the workbooks
86
- # :alert -> give control to Excel
87
- # :hard kill the Excel instance hard (default: false)
88
- def close(options = {})
89
- options = {
90
- :if_unsaved => :raise,
91
- :hard => false
92
- }.merge(options)
93
- unsaved_books = self.unsaved_workbooks
94
- unless unsaved_books.empty?
127
+ def self.init
128
+ @@hwnd2excel = {}
129
+ end
130
+
131
+ private
132
+
133
+ def self.manage_unsaved_workbooks(excel, options)
134
+ #this_excel = excel == 0 ? self : excel
135
+ unsaved_workbooks = []
136
+ begin
137
+ excel.Workbooks.each {|w| unsaved_workbooks << w unless (w.Saved || w.ReadOnly)}
138
+ rescue RuntimeError => msg
139
+ t "RuntimeError: #{msg.message}"
140
+ raise ExcelErrorOpen, "Excel instance not alive or damaged" if msg.message =~ /failed to get Dispatch Interface/
141
+ end
142
+ unless unsaved_workbooks.empty?
95
143
  case options[:if_unsaved]
96
144
  when :raise
97
145
  raise ExcelErrorClose, "Excel contains unsaved workbooks"
@@ -99,30 +147,109 @@ module RobustExcelOle
99
147
  unsaved_workbooks.each do |workbook|
100
148
  workbook.Save
101
149
  end
102
- close_excel(:hard => options[:hard])
103
150
  when :forget
104
- close_excel(:hard => options[:hard])
151
+ # nothing
105
152
  when :alert
106
- with_displayalerts true do
107
- unsaved_workbooks.each do |workbook|
108
- workbook.Save
109
- end
110
- close_excel(:hard => options[:hard])
111
- end
153
+ excel.DisplayAlerts = true
112
154
  else
113
- raise ExcelErrorClose, ":if_unsaved: invalid option: #{options[:if_unsaved]}"
155
+ raise ExcelErrorClose, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}"
114
156
  end
115
- else
116
- close_excel(:hard => options[:hard])
117
157
  end
118
- raise ExcelUserCanceled, "close: canceled by user" if options[:if_unsaved] == :alert && self.unsaved_workbooks
158
+ end
159
+
160
+ # closes one Excel instance to which one was connected
161
+ def self.close_one_excel(options={})
162
+ excel = current_excel
163
+ 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__)
168
+ end
169
+
170
+ def self.close_excel_ole_instance(ole_excel)
171
+ @@hwnd2excel.delete(ole_excel.Hwnd)
172
+ excel = ole_excel
173
+ ole_excel = nil
174
+ begin
175
+ excel.Workbooks.Close
176
+ excel_hwnd = excel.HWnd
177
+ excel.Quit
178
+ weak_excel_ref = WeakRef.new(excel)
179
+ excel = nil
180
+ GC.start
181
+ sleep 0.2
182
+ if weak_excel_ref.weakref_alive? then
183
+ #if WIN32OLE.ole_reference_count(weak_xlapp) > 0
184
+ begin
185
+ weak_excel_ref.ole_free
186
+ t "successfully ole_freed #{weak_excel_ref}"
187
+ rescue
188
+ t "could not do ole_free on #{weak_excel_ref}"
189
+ end
190
+ end
191
+ hwnd2excel(excel_hwnd).die rescue nil
192
+ @@hwnd2excel.delete(excel_hwnd)
193
+ rescue => e
194
+ t "Error when closing Excel: #{e.message}"
195
+ #t e.backtrace
196
+ end
197
+ free_all_ole_objects
198
+ end
199
+
200
+ # frees all OLE objects in the object space
201
+ def self.free_all_ole_objects
202
+ anz_objekte = 0
203
+ ObjectSpace.each_object(WIN32OLE) do |o|
204
+ anz_objekte += 1
205
+ #t [:Name, (o.Name rescue (o.Count rescue "no_name"))]
206
+ #t [:ole_object_name, (o.ole_object_name rescue nil)]
207
+ #t [:methods, (o.ole_methods rescue nil)] unless (o.Name rescue false)
208
+ #t o.ole_type rescue nil
209
+ begin
210
+ o.ole_free
211
+ #t "olefree OK"
212
+ rescue
213
+ #t "olefree_error: #{$!}"
214
+ #t $!.backtrace.first(9).join "\n"
215
+ end
216
+ end
217
+ t "went through #{anz_objekte} OLE objects"
218
+ end
219
+
220
+ # sets this Excel instance to nil
221
+ def die
222
+ @ole_excel = nil
223
+ end
224
+
225
+ public
226
+
227
+ # closes the Excel
228
+ # :if_unsaved if unsaved workbooks are open in an Excel instance
229
+ # :raise (default) -> raises an exception
230
+ # :save -> saves the workbooks before closing
231
+ # :forget -> closes the Excel instance without saving the workbooks
232
+ # :alert -> gives control to Excel
233
+ # :hard kill the Excel instance hard (default: false)
234
+ def close(options = {})
235
+ options = {
236
+ :if_unsaved => :raise,
237
+ :hard => false
238
+ }.merge(options)
239
+ self.class.manage_unsaved_workbooks(@ole_excel, options)
240
+ close_excel(options)
119
241
  end
120
242
 
121
243
  private
122
244
 
123
245
  def close_excel(options)
124
- excel = @this_excel
125
- excel.Workbooks.Close
246
+ excel = @ole_excel
247
+ begin
248
+ excel.Workbooks.Close
249
+ rescue WIN32OLERuntimeError => msg
250
+ raise ExcelUserCanceled, "close: canceled by user" if msg.message =~ /80020009/ &&
251
+ options[:if_unsaved] == :alert && (not self.unsaved_workbooks.empty?)
252
+ end
126
253
  excel_hwnd = excel.HWnd
127
254
  excel.Quit
128
255
  weak_excel_ref = WeakRef.new(excel)
@@ -133,55 +260,95 @@ module RobustExcelOle
133
260
  #if WIN32OLE.ole_reference_count(weak_xlapp) > 0
134
261
  begin
135
262
  weak_excel_ref.ole_free
136
- puts "successfully ole_freed #{weak_excel_ref}"
137
- rescue
138
- puts "could not do ole_free on #{weak_excel_ref}"
263
+ t "successfully ole_freed #{weak_excel_ref}"
264
+ rescue => msg
265
+ t "#{msg.message}"
266
+ t "could not do ole_free on #{weak_excel_ref}"
139
267
  end
140
268
  end
141
269
  hwnd2excel(excel_hwnd).die rescue nil
142
- #@@hwnd2excel[excel_hwnd] = nil
143
- #Excel.free_all_ole_objects
270
+ @@hwnd2excel.delete(excel_hwnd)
144
271
  if options[:hard] then
272
+ Excel.free_all_ole_objects
145
273
  process_id = Win32API.new("user32", "GetWindowThreadProcessId", ["I","P"], "I")
146
274
  pid_puffer = " " * 32
147
275
  process_id.call(excel_hwnd, pid_puffer)
148
276
  pid = pid_puffer.unpack("L")[0]
149
- Process.kill("KILL", pid)
277
+ Process.kill("KILL", pid) rescue nil
150
278
  end
151
279
  end
152
280
 
153
281
  public
154
282
 
155
- def excel
156
- self
283
+ # kill all Excel instances
284
+ def self.kill_all
285
+ procs = WIN32OLE.connect("winmgmts:\\\\.")
286
+ processes = procs.InstancesOf("win32_process")
287
+ number = processes.select{|p| (p.name == "EXCEL.EXE")}.size
288
+ procs.InstancesOf("win32_process").each do |p|
289
+ Process.kill('KILL', p.processid) if p.name == "EXCEL.EXE"
290
+ end
291
+ number
157
292
  end
158
293
 
159
- # generate, save and close an empty workbook
160
- def generate_workbook file_name
161
- self.Workbooks.Add
162
- empty_workbook = self.Workbooks.Item(self.Workbooks.Count)
163
- filename = RobustExcelOle::absolute_path(file_name).gsub("/","\\")
164
- unless File.exists?(filename)
165
- begin
166
- empty_workbook.SaveAs(filename)
167
- rescue WIN32OLERuntimeError => msg
168
- if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
169
- raise ExcelErrorSave, "could not save workbook with filename #{file_name}"
170
- else
171
- # todo some time: find out when this occurs :
172
- raise ExcelErrorSaveUnknown, "unknown WIN32OELERuntimeError with filename #{file_name}: \n#{msg.message}"
294
+ # provide Excel objects
295
+ # (so far restricted to all Excel instances opened with RobustExcelOle,
296
+ # not for Excel instances opened by the user)
297
+ def self.excel_processes
298
+ pid2excel = {}
299
+ @@hwnd2excel.each do |hwnd,wr_excel|
300
+ process_id = Win32API.new("user32", "GetWindowThreadProcessId", ["I","P"], "I")
301
+ pid_puffer = " " * 32
302
+ process_id.call(hwnd, pid_puffer)
303
+ pid = pid_puffer.unpack("L")[0]
304
+ pid2excel[pid] = wr_excel
305
+ end
306
+ procs = WIN32OLE.connect("winmgmts:\\\\.")
307
+ processes = procs.InstancesOf("win32_process")
308
+ result = []
309
+ processes.each do |p|
310
+ if p.name == "EXCEL.EXE"
311
+ if pid2excel.include?(p.processid)
312
+ excel = pid2excel[p.processid].__getobj__
313
+ result << excel
173
314
  end
174
- end
315
+ # how to connect with an Excel instance that is not in hwnd2excel (uplifting Excel)?
316
+ #excel = Excel.uplift unless (excel && excel.alive?)
317
+ end
175
318
  end
176
- empty_workbook
319
+ result
320
+ end
321
+
322
+ def excel
323
+ self
177
324
  end
178
325
 
179
326
  def self.hwnd2excel(hwnd)
180
- @@hwnd2excel[hwnd]
327
+ excel_weakref = @@hwnd2excel[hwnd]
328
+ if excel_weakref
329
+ if excel_weakref.weakref_alive?
330
+ excel_weakref.__getobj__
331
+ else
332
+ t "dead reference to an Excel"
333
+ begin
334
+ @@hwnd2excel.delete(hwnd)
335
+ rescue
336
+ t "Warning: deleting dead reference failed! (hwnd: #{hwnd.inspect})"
337
+ end
338
+ end
339
+ end
181
340
  end
182
341
 
183
342
  def hwnd
184
- self.Hwnd
343
+ self.Hwnd rescue nil
344
+ end
345
+
346
+ def self.print_hwnd2excel
347
+ @@hwnd2excel.each do |hwnd,wr_excel|
348
+ excel_string = (wr_excel.weakref_alive? ? wr_excel.__getobj__.to_s : "weakref not alive")
349
+ printf("hwnd: %8i => excel: %s\n", hwnd, excel_string)
350
+ end
351
+ @@hwnd2excel.size
185
352
  end
186
353
 
187
354
  # returns true, if the Excel instances are alive and identical, false otherwise
@@ -189,66 +356,86 @@ module RobustExcelOle
189
356
  self.Hwnd == other_excel.Hwnd if other_excel.is_a?(Excel) && self.alive? && other_excel.alive?
190
357
  end
191
358
 
192
- # returns true, if the Excel instances responds to VVA methods, false otherwise
359
+ # returns true, if the Excel instances responds to VBA methods, false otherwise
193
360
  def alive?
194
- @this_excel.Name
361
+ @ole_excel.Name
195
362
  true
196
363
  rescue
197
- #puts $!.message
364
+ #t $!.message
198
365
  false
199
366
  end
200
367
 
201
368
  def print_workbooks
202
- self.Workbooks.each {|w| puts w.Name}
369
+ self.Workbooks.each {|w| t "#{w.Name} #{w}"}
203
370
  end
204
371
 
372
+ # returns all unsaved workbooks
205
373
  def unsaved_workbooks
206
374
  result = []
207
375
  begin
208
376
  self.Workbooks.each {|w| result << w unless (w.Saved || w.ReadOnly)}
209
377
  rescue RuntimeError => msg
210
- puts "RuntimeError: #{msg.message}"
378
+ t "RuntimeError: #{msg.message}"
211
379
  raise ExcelErrorOpen, "Excel instance not alive or damaged" if msg.message =~ /failed to get Dispatch Interface/
212
380
  end
213
- result
381
+ result
214
382
  end
215
383
  # yields different WIN32OLE objects than book.workbook
216
- #self.class.extend Enumerable
217
384
  #self.class.map {|w| (not w.Saved)}
218
385
 
219
- # set DisplayAlerts in a block
386
+ # empty workbook is generated, saved and closed
387
+ def generate_workbook file_name
388
+ self.Workbooks.Add
389
+ empty_workbook = self.Workbooks.Item(self.Workbooks.Count)
390
+ filename = RobustExcelOle::absolute_path(file_name).gsub("/","\\")
391
+ unless File.exists?(filename)
392
+ begin
393
+ empty_workbook.SaveAs(filename)
394
+ rescue WIN32OLERuntimeError => msg
395
+ if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
396
+ raise ExcelErrorSave, "could not save workbook with filename #{file_name.inspect}"
397
+ else
398
+ # todo some time: find out when this occurs :
399
+ raise ExcelErrorSaveUnknown, "unknown WIN32OELERuntimeError with filename #{file_name.inspect}: \n#{msg.message}"
400
+ end
401
+ end
402
+ end
403
+ empty_workbook
404
+ end
405
+
406
+ # sets DisplayAlerts in a block
220
407
  def with_displayalerts displayalerts_value
221
- old_displayalerts = @this_excel.DisplayAlerts
222
- @this_excel.DisplayAlerts = displayalerts_value
408
+ old_displayalerts = @ole_excel.DisplayAlerts
409
+ @ole_excel.DisplayAlerts = displayalerts_value
223
410
  begin
224
411
  yield self
225
412
  ensure
226
- @this_excel.DisplayAlerts = old_displayalerts
413
+ @ole_excel.DisplayAlerts = old_displayalerts if alive?
227
414
  end
228
415
  end
229
416
 
230
- # enable DisplayAlerts in the current Excel instance
417
+ # enables DisplayAlerts in the current Excel instance
231
418
  def displayalerts= displayalerts_value
232
- @this_excel.DisplayAlerts = displayalerts_value
419
+ @ole_excel.DisplayAlerts = displayalerts_value
233
420
  end
234
421
 
235
422
  # return if in the current Excel instance DisplayAlerts is enabled
236
423
  def displayalerts
237
- @this_excel.DisplayAlerts
424
+ @ole_excel.DisplayAlerts
238
425
  end
239
426
 
240
- # make the current Excel instance visible or invisible
427
+ # makes the current Excel instance visible or invisible
241
428
  def visible= visible_value
242
- @this_excel.Visible = visible_value
429
+ @ole_excel.Visible = visible_value
243
430
  end
244
431
 
245
- # return if the current Excel instance is visible
432
+ # returns whether the current Excel instance is visible
246
433
  def visible
247
- @this_excel.Visible
248
- end
434
+ @ole_excel.Visible
435
+ end
249
436
 
250
437
  def to_s
251
- "#<" + "EXCEL:#{hwnd_xxxx}" + ("#{"not alive" unless self.alive?}") + ">"
438
+ "#<Excel: " + "#{hwnd}" + ("#{"not alive" unless self.alive?}") + ">"
252
439
  end
253
440
 
254
441
  def inspect
@@ -257,109 +444,14 @@ module RobustExcelOle
257
444
 
258
445
  private
259
446
 
260
- # closes one Excel instance
261
- def self.close_one_excel(options={})
262
- excel = current_excel
263
- if excel then
264
- weak_ole_excel = WeakRef.new(excel)
265
- excel = nil
266
- close_excel_ole_instance(weak_ole_excel.__getobj__)
267
- end
268
- end
269
-
270
- def self.close_excel_ole_instance(ole_excel)
271
- excel = ole_excel
272
- ole_excel = nil
273
- begin
274
- excel.Workbooks.Close
275
- excel_hwnd = excel.HWnd
276
- excel.Quit
277
- weak_excel_ref = WeakRef.new(excel)
278
- excel = nil
279
- GC.start
280
- sleep 0.2
281
- #sleep 0.1
282
- if weak_excel_ref.weakref_alive? then
283
- #if WIN32OLE.ole_reference_count(weak_xlapp) > 0
284
- begin
285
- weak_excel_ref.ole_free
286
- puts "successfully ole_freed #{weak_excel_ref}"
287
- rescue
288
- puts "could not do ole_free on #{weak_excel_ref}"
289
- end
290
- end
291
-
292
- hwnd2excel(excel_hwnd).die rescue nil
293
- #@@hwnd2excel[excel_hwnd] = nil
294
-
295
- rescue => e
296
- puts "Error when closing Excel: " + e.message
297
- #puts e.backtrace
298
- end
299
-
300
-
301
- free_all_ole_objects
302
-
303
- return
304
- process_id = Win32API.new("user32", "GetWindowThreadProcessId", ["I","P"], "I")
305
- pid_puffer = " " * 32
306
- process_id.call(excel_hwnd, pid_puffer)
307
- pid = pid_puffer.unpack("L")[0]
308
- Process.kill("KILL", pid)
309
- end
310
-
311
- # frees all OLE objects in the object space
312
- def self.free_all_ole_objects
313
- anz_objekte = 0
314
- ObjectSpace.each_object(WIN32OLE) do |o|
315
- anz_objekte += 1
316
- #p [:Name, (o.Name rescue (o.Count rescue "no_name"))]
317
- #p [:ole_object_name, (o.ole_object_name rescue nil)]
318
- #p [:methods, (o.ole_methods rescue nil)] unless (o.Name rescue false)
319
- #puts o.ole_type rescue nil
320
- #trc_info :obj_hwnd, o.HWnd rescue nil
321
- #trc_info :obj_Parent, o.Parent rescue nil
322
- begin
323
- o.ole_free
324
- #puts "olefree OK"
325
- rescue
326
- #puts "olefree_error: #{$!}"
327
- #puts $!.backtrace.first(9).join "\n"
328
- end
329
- end
330
- puts "went through #{anz_objekte} OLE objects"
331
- end
332
-
333
- # returns the current Excel instance
334
- def self.current_excel # :nodoc: #
335
- result = WIN32OLE.connect('Excel.Application') rescue nil
336
- if result
337
- begin
338
- result.Visible # send any method, just to see if it responds
339
- rescue
340
- puts "dead excel app " + ("Window-handle = #{result.HWnd}" rescue "without window handle")
341
- return nil
342
- end
343
- end
344
- result
345
- end
346
-
347
- def hwnd_xxxx
348
- self.HWnd #rescue Win32 nil
349
- end
350
-
351
- # set this Excel instance to nil
352
- def die
353
- @this_excel = nil
354
- end
355
-
356
447
  def method_missing(name, *args)
357
448
  if name.to_s[0,1] =~ /[A-Z]/
358
- begin
359
- @this_excel.send(name, *args)
449
+ begin
450
+ raise ExcelError, "method missing: Excel not alive" unless alive?
451
+ @ole_excel.send(name, *args)
360
452
  rescue WIN32OLERuntimeError => msg
361
453
  if msg.message =~ /unknown property or method/
362
- raise VBAMethodMissingError, "unknown VBA property or method #{name}"
454
+ raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
363
455
  else
364
456
  raise msg
365
457
  end