robust_excel_ole 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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