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.
- data/Changelog +33 -0
- data/README.rdoc +97 -35
- data/README_detail.rdoc +121 -53
- data/examples/edit_sheets/example_naming.rb +1 -0
- data/lib/reo_console.rb +52 -0
- data/lib/robust_excel_ole.rb +41 -7
- data/lib/robust_excel_ole/book.rb +327 -193
- data/lib/robust_excel_ole/bookstore.rb +33 -17
- data/lib/robust_excel_ole/excel.rb +280 -188
- data/lib/robust_excel_ole/sheet.rb +29 -16
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/spec_helper.rb +1 -1
- data/reo.bat +1 -1
- data/spec/book_specs/book_all_spec.rb +22 -0
- data/spec/{book_close_spec.rb → book_specs/book_close_spec.rb} +14 -14
- data/spec/{book_misc_spec.rb → book_specs/book_misc_spec.rb} +38 -29
- data/spec/{book_open_spec.rb → book_specs/book_open_spec.rb} +40 -16
- data/spec/{book_save_spec.rb → book_specs/book_save_spec.rb} +173 -12
- data/spec/{book_sheet_spec.rb → book_specs/book_sheet_spec.rb} +8 -4
- data/spec/{book_spec.rb → book_specs/book_spec.rb} +456 -187
- data/spec/{book_subclass_spec.rb → book_specs/book_subclass_spec.rb} +4 -4
- data/spec/{book_unobtr_spec.rb → book_specs/book_unobtr_spec.rb} +64 -4
- data/spec/bookstore_spec.rb +11 -4
- data/spec/cell_spec.rb +7 -5
- data/spec/data/another_workbook.xls +0 -0
- data/spec/data/different_workbook.xls +0 -0
- data/spec/data/more_data/workbook.xls +0 -0
- data/spec/data/workbook.xls +0 -0
- data/spec/excel_spec.rb +367 -87
- data/spec/range_spec.rb +6 -4
- data/spec/robust_excel_ole_spec.rb +113 -0
- data/spec/sheet_spec.rb +42 -6
- metadata +16 -12
@@ -6,18 +6,20 @@ module RobustExcelOle
|
|
6
6
|
class Bookstore
|
7
7
|
|
8
8
|
def initialize
|
9
|
-
@filename2books
|
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
|
17
|
-
# otherwise
|
18
|
-
# :prefer_excel
|
19
|
-
# otherwise
|
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
|
-
|
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
|
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
|
-
|
96
|
+
t "@filename2books:"
|
80
97
|
if @filename2books
|
81
98
|
@filename2books.each do |filename,books|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
89
|
-
p "excel: #{book.excel}"
|
90
|
-
p "alive: #{book.alive?}"
|
106
|
+
t "#{book}"
|
91
107
|
else
|
92
|
-
|
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
|
23
|
-
#
|
24
|
-
# :
|
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(:@
|
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) ->
|
63
|
-
# :save ->
|
64
|
-
# :forget ->
|
65
|
-
# :alert ->
|
66
|
-
# :hard
|
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
|
-
|
74
|
-
close_one_excel
|
117
|
+
close_one_excel(options)
|
75
118
|
GC.start
|
76
119
|
sleep 0.3
|
77
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
151
|
+
# nothing
|
105
152
|
when :alert
|
106
|
-
|
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
|
-
|
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 = @
|
125
|
-
|
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
|
-
|
137
|
-
rescue
|
138
|
-
|
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
|
-
|
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
|
-
|
156
|
-
|
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
|
-
#
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
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
|
-
|
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
|
359
|
+
# returns true, if the Excel instances responds to VBA methods, false otherwise
|
193
360
|
def alive?
|
194
|
-
@
|
361
|
+
@ole_excel.Name
|
195
362
|
true
|
196
363
|
rescue
|
197
|
-
#
|
364
|
+
#t $!.message
|
198
365
|
false
|
199
366
|
end
|
200
367
|
|
201
368
|
def print_workbooks
|
202
|
-
self.Workbooks.each {|w|
|
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
|
-
|
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
|
-
#
|
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 = @
|
222
|
-
@
|
408
|
+
old_displayalerts = @ole_excel.DisplayAlerts
|
409
|
+
@ole_excel.DisplayAlerts = displayalerts_value
|
223
410
|
begin
|
224
411
|
yield self
|
225
412
|
ensure
|
226
|
-
@
|
413
|
+
@ole_excel.DisplayAlerts = old_displayalerts if alive?
|
227
414
|
end
|
228
415
|
end
|
229
416
|
|
230
|
-
#
|
417
|
+
# enables DisplayAlerts in the current Excel instance
|
231
418
|
def displayalerts= displayalerts_value
|
232
|
-
@
|
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
|
-
@
|
424
|
+
@ole_excel.DisplayAlerts
|
238
425
|
end
|
239
426
|
|
240
|
-
#
|
427
|
+
# makes the current Excel instance visible or invisible
|
241
428
|
def visible= visible_value
|
242
|
-
@
|
429
|
+
@ole_excel.Visible = visible_value
|
243
430
|
end
|
244
431
|
|
245
|
-
#
|
432
|
+
# returns whether the current Excel instance is visible
|
246
433
|
def visible
|
247
|
-
@
|
248
|
-
end
|
434
|
+
@ole_excel.Visible
|
435
|
+
end
|
249
436
|
|
250
437
|
def to_s
|
251
|
-
"#<" + "
|
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
|
-
|
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
|