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.
- 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
|