robust_excel_ole 1.4 → 1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog +6 -0
- data/README.rdoc +7 -1
- data/docs/README_ranges.rdoc +1 -1
- data/lib/reo_console.rb +19 -17
- data/lib/robust_excel_ole.rb +1 -2
- data/lib/robust_excel_ole/bookstore.rb +45 -45
- data/lib/robust_excel_ole/cell.rb +11 -12
- data/lib/robust_excel_ole/cygwin.rb +11 -11
- data/lib/robust_excel_ole/excel.rb +218 -201
- data/lib/robust_excel_ole/general.rb +23 -21
- data/lib/robust_excel_ole/range.rb +18 -19
- data/lib/robust_excel_ole/reo_common.rb +95 -82
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +199 -205
- data/lib/robust_excel_ole/worksheet.rb +33 -56
- data/lib/spec_helper.rb +3 -3
- data/spec/workbook_spec.rb +18 -0
- data/spec/workbook_specs/workbook_misc_spec.rb +2 -2
- data/spec/worksheet_spec.rb +18 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d86cf14a83a7f64c9c81e1a5473a13e66ac8be2
|
4
|
+
data.tar.gz: d7f78aba06eac6432722f36b4d753ed1b1cc94a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c97c2fe5da0196f6021ad6a4bf1f62b1a1b7b1dd7c35df1699fd6f61894b5f1394ef8950cc2f1588775188a2909afe341400d1b7574c461b6990abb06c9ed4d
|
7
|
+
data.tar.gz: e639929ed6031c0c4e041350a95d5b95aca527b956a917b4cad5ee578d301925599927d4c532dd80958a5306dce543fd4b27dbd2a0a6e36280484a3bd43ab23b
|
data/Changelog
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [1.4.1]
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
- renamed Worksheet#name2range to ReoCommon#range
|
8
|
+
|
4
9
|
## [1.4]
|
5
10
|
|
6
11
|
### Changed
|
7
12
|
- renamed Book to Workbook, Sheet to Worksheet
|
13
|
+
- General#to_reo
|
8
14
|
|
9
15
|
## [1.3.1]
|
10
16
|
|
data/README.rdoc
CHANGED
@@ -285,6 +285,12 @@ and set another value to that range.
|
|
285
285
|
|
286
286
|
For more details about reading and writing contents of cells and ranges see {README_ranges}[https://github.com/Thomas008/robust_excel_ole/blob/master/docs/README_ranges.rdoc]
|
287
287
|
|
288
|
+
=== More things
|
289
|
+
|
290
|
+
You can convert a win32ole object into a RobustExcelOle object.
|
291
|
+
|
292
|
+
range = sheet.Names.Item("firstcell").to_reo
|
293
|
+
|
288
294
|
=== Examples
|
289
295
|
|
290
296
|
You can run the examples included in the directory +examples+, e.g.
|
@@ -297,7 +303,7 @@ This project RobustExcelOle is work in progress. We are happy to implement furth
|
|
297
303
|
|
298
304
|
RobustExcelOle is being tested for Excel 2010. It can be used for any recent Excel Office version.
|
299
305
|
|
300
|
-
|
306
|
+
RobustExcelOle has been optimised with help of the rubocop and the rcov tool.
|
301
307
|
|
302
308
|
== Support
|
303
309
|
|
data/docs/README_ranges.rdoc
CHANGED
data/lib/reo_console.rb
CHANGED
@@ -1,52 +1,54 @@
|
|
1
|
-
#require '../robust_excel_ole/lib/robust_excel_ole'
|
1
|
+
# require '../robust_excel_ole/lib/robust_excel_ole'
|
2
2
|
include REO
|
3
|
-
#include RobustExcelOle
|
3
|
+
# include RobustExcelOle
|
4
4
|
include General
|
5
5
|
|
6
6
|
require 'irb/completion'
|
7
7
|
require 'irb/ext/save-history'
|
8
8
|
|
9
|
-
ARGV.concat [
|
10
|
-
|
11
|
-
|
9
|
+
ARGV.concat ['--readline',
|
10
|
+
'--prompt-mode',
|
11
|
+
'simple']
|
12
12
|
|
13
13
|
# 250 entries in the list
|
14
14
|
IRB.conf[:SAVE_HISTORY] = 250
|
15
15
|
|
16
16
|
# Store results in home directory with specified file name
|
17
|
-
#IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-history"
|
17
|
+
# IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-history"
|
18
18
|
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.reo-history"
|
19
19
|
|
20
20
|
module Readline # :nodoc: #
|
21
21
|
module Hist # :nodoc: #
|
22
22
|
LOG = IRB.conf[:HISTORY_FILE]
|
23
|
-
# LOG = "#{ENV['HOME']}/.irb-history"
|
23
|
+
# LOG = "#{ENV['HOME']}/.irb-history"
|
24
24
|
|
25
25
|
def self.write_log(line)
|
26
|
-
File.open(LOG, 'ab')
|
27
|
-
"}
|
26
|
+
File.open(LOG, 'ab') do |f|
|
27
|
+
f << "#{line}
|
28
|
+
"
|
29
|
+
end
|
28
30
|
end
|
29
31
|
|
30
32
|
def self.start_session_log
|
31
|
-
timestamp = proc{ Time.now.strftime(
|
32
|
-
class <<timestamp
|
33
|
-
|
34
|
-
end
|
35
|
-
write_log(
|
33
|
+
timestamp = proc { Time.now.strftime('%Y-%m-%d, %H:%M:%S') }
|
34
|
+
class <<timestamp # :nodoc: #
|
35
|
+
alias_method :to_s, :call
|
36
|
+
end
|
37
|
+
write_log("###### session start: #{timestamp}")
|
36
38
|
at_exit { write_log("###### session stop: #{timestamp}") }
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
|
-
alias
|
42
|
+
alias old_readline readline
|
41
43
|
def readline(*args)
|
42
44
|
ln = old_readline(*args)
|
43
45
|
begin
|
44
46
|
Hist.write_log(ln)
|
45
|
-
rescue
|
47
|
+
rescue StandardError
|
46
48
|
end
|
47
49
|
ln
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
51
53
|
Readline::Hist.start_session_log
|
52
|
-
puts
|
54
|
+
puts 'REO console started'
|
data/lib/robust_excel_ole.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'win32ole'
|
2
2
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/reo_common')
|
3
3
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/general')
|
4
4
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/excel')
|
@@ -10,4 +10,3 @@ require File.join(File.dirname(__FILE__), 'robust_excel_ole/range')
|
|
10
10
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/cygwin') if RUBY_PLATFORM =~ /cygwin/
|
11
11
|
#+#require "robust_excel_ole/version"
|
12
12
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/version')
|
13
|
-
#
|
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
1
|
# -*- coding: utf-8 -*-
|
3
2
|
|
4
3
|
module RobustExcelOle
|
5
4
|
|
6
5
|
class Bookstore < REOCommon
|
7
|
-
|
8
6
|
def initialize
|
9
|
-
@filename2books ||= Hash.new {|hash, key| hash[key] = [] }
|
7
|
+
@filename2books ||= Hash.new { |hash, key| hash[key] = [] }
|
10
8
|
@hidden_excel_instance = nil
|
11
9
|
end
|
12
10
|
|
@@ -20,50 +18,53 @@ module RobustExcelOle
|
|
20
18
|
# options: :prefer_writable returns the writable book, if it is open (default: true)
|
21
19
|
# otherwise returns the book according to the preference order mentioned above
|
22
20
|
# :prefer_excel returns the book in the given Excel instance, if it exists,
|
23
|
-
# otherwise proceeds according to prefer_writable
|
24
|
-
def fetch(filename, options = {:prefer_writable => true })
|
21
|
+
# otherwise proceeds according to prefer_writable
|
22
|
+
def fetch(filename, options = { :prefer_writable => true })
|
25
23
|
return nil unless filename
|
26
|
-
|
27
|
-
|
24
|
+
|
25
|
+
filename = General.absolute_path(filename)
|
26
|
+
filename_key = General.canonize(filename)
|
28
27
|
weakref_books = @filename2books[filename_key]
|
29
28
|
return nil unless weakref_books
|
30
|
-
|
31
|
-
|
29
|
+
|
30
|
+
result = open_book = closed_book = nil
|
31
|
+
weakref_books = weakref_books.map { |wr_book| wr_book if wr_book.weakref_alive? }.compact
|
32
32
|
@filename2books[filename_key] = weakref_books
|
33
33
|
weakref_books.each do |wr_book|
|
34
|
-
if
|
35
|
-
#trace "warn: this should never happen"
|
36
|
-
begin
|
34
|
+
if !wr_book.weakref_alive?
|
35
|
+
# trace "warn: this should never happen"
|
36
|
+
begin
|
37
37
|
@filename2books[filename_key].delete(wr_book)
|
38
|
-
rescue
|
39
|
-
#trace "#{$!.message}"
|
40
|
-
#trace "Warning: deleting dead reference failed: file: #{filename.inspect}"
|
38
|
+
rescue
|
39
|
+
# trace "#{$!.message}"
|
40
|
+
# trace "Warning: deleting dead reference failed: file: #{filename.inspect}"
|
41
41
|
end
|
42
42
|
else
|
43
43
|
book = wr_book.__getobj__
|
44
44
|
next if book.excel == try_hidden_excel
|
45
|
+
|
45
46
|
if options[:prefer_excel] && book.excel == options[:prefer_excel]
|
46
47
|
result = book
|
47
|
-
break
|
48
|
+
break
|
48
49
|
end
|
49
50
|
if book.alive?
|
50
51
|
open_book = book
|
51
|
-
break if
|
52
|
+
break if book.writable && options[:prefer_writable]
|
52
53
|
else
|
53
54
|
closed_book = book
|
54
55
|
end
|
55
56
|
end
|
56
57
|
end
|
57
|
-
result
|
58
|
-
result
|
58
|
+
result ||= (open_book || closed_book)
|
59
|
+
result
|
59
60
|
end
|
60
61
|
|
61
62
|
# stores a workbook
|
62
63
|
# @param [Workbook] book a given book
|
63
64
|
def store(book)
|
64
|
-
filename_key = General
|
65
|
+
filename_key = General.canonize(book.filename)
|
65
66
|
if book.stored_filename
|
66
|
-
old_filename_key = General
|
67
|
+
old_filename_key = General.canonize(book.stored_filename)
|
67
68
|
# deletes the weak reference to the book
|
68
69
|
@filename2books[old_filename_key].delete(book)
|
69
70
|
end
|
@@ -72,9 +73,9 @@ module RobustExcelOle
|
|
72
73
|
end
|
73
74
|
|
74
75
|
# creates and returns a separate Excel instance with Visible and DisplayAlerts equal false
|
75
|
-
def hidden_excel
|
76
|
-
unless
|
77
|
-
@hidden_excel_instance = WeakRef.new(Excel.create)
|
76
|
+
def hidden_excel # :nodoc: #
|
77
|
+
unless @hidden_excel_instance && @hidden_excel_instance.weakref_alive? && @hidden_excel_instance.__getobj__.alive?
|
78
|
+
@hidden_excel_instance = WeakRef.new(Excel.create)
|
78
79
|
end
|
79
80
|
@hidden_excel_instance.__getobj__
|
80
81
|
end
|
@@ -83,50 +84,49 @@ module RobustExcelOle
|
|
83
84
|
def books
|
84
85
|
result = []
|
85
86
|
if @filename2books
|
86
|
-
@filename2books.each do |
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
@filename2books.each do |_filename,books|
|
88
|
+
next if books.empty?
|
89
|
+
|
90
|
+
books.each do |wr_book|
|
91
|
+
result << wr_book.__getobj__ if wr_book.weakref_alive?
|
91
92
|
end
|
92
|
-
end
|
93
|
+
end
|
93
94
|
end
|
94
95
|
result
|
95
96
|
end
|
96
97
|
|
97
|
-
|
98
|
+
private
|
98
99
|
|
99
|
-
def try_hidden_excel
|
100
|
-
@hidden_excel_instance.__getobj__ if
|
100
|
+
def try_hidden_excel # :nodoc: #
|
101
|
+
@hidden_excel_instance.__getobj__ if @hidden_excel_instance && @hidden_excel_instance.weakref_alive? && @hidden_excel_instance.__getobj__.alive?
|
101
102
|
end
|
102
103
|
|
103
|
-
|
104
|
+
public
|
104
105
|
|
105
106
|
# prints the book store
|
106
|
-
def print
|
107
|
-
#trace "@filename2books:"
|
107
|
+
def print # :nodoc: #
|
108
|
+
# trace "@filename2books:"
|
108
109
|
if @filename2books
|
109
|
-
@filename2books.each do |
|
110
|
-
#trace " filename: #{filename}"
|
111
|
-
#trace " books:"
|
112
|
-
if books.empty?
|
113
|
-
#trace " []"
|
110
|
+
@filename2books.each do |_filename,books|
|
111
|
+
# trace " filename: #{filename}"
|
112
|
+
# trace " books:"
|
113
|
+
if books.empty?
|
114
|
+
# trace " []"
|
114
115
|
else
|
115
116
|
books.each do |book|
|
116
117
|
if book.weakref_alive?
|
117
|
-
#trace "#{book}"
|
118
|
+
# trace "#{book}"
|
118
119
|
else # this should never happen
|
119
|
-
#trace "weakref not alive"
|
120
|
+
# trace "weakref not alive"
|
120
121
|
end
|
121
122
|
end
|
122
123
|
end
|
123
124
|
end
|
124
125
|
end
|
125
126
|
end
|
126
|
-
|
127
127
|
end
|
128
128
|
|
129
129
|
class BookstoreError < WIN32OLERuntimeError # :nodoc: #
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
end
|
@@ -5,28 +5,27 @@ module RobustExcelOle
|
|
5
5
|
attr_reader :cell
|
6
6
|
|
7
7
|
def initialize(win32_cell)
|
8
|
-
if win32_cell.MergeCells
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
@cell = if win32_cell.MergeCells
|
9
|
+
win32_cell.MergeArea.Item(1,1)
|
10
|
+
else
|
11
|
+
win32_cell
|
12
|
+
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def method_missing(name, *args)
|
16
|
-
if name.to_s[0,1] =~ /[A-Z]/
|
15
|
+
def method_missing(name, *args) # :nodoc: #
|
16
|
+
#if name.to_s[0,1] =~ /[A-Z]/
|
17
17
|
begin
|
18
18
|
@cell.send(name, *args)
|
19
19
|
rescue WIN32OLERuntimeError => msg
|
20
20
|
if msg.message =~ /unknown property or method/
|
21
21
|
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
22
|
-
else
|
22
|
+
else
|
23
23
|
raise msg
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
# else
|
27
|
+
# super
|
28
|
+
# end
|
29
29
|
end
|
30
|
-
|
31
30
|
end
|
32
31
|
end
|
@@ -11,27 +11,27 @@ module RobustExcelOle
|
|
11
11
|
@conv_to_win32_path =
|
12
12
|
Win32API.new('cygwin1.dll', 'cygwin_conv_to_win32_path', 'PP', 'I')
|
13
13
|
|
14
|
-
def cygpath(options, path)
|
14
|
+
def cygpath(options, path) # :nodoc: #
|
15
15
|
absolute = shortname = false
|
16
16
|
func = nil
|
17
|
-
options.delete(" \t-").chars
|
17
|
+
options.delete(" \t-").chars do |opt|
|
18
18
|
case opt
|
19
|
-
when
|
19
|
+
when 'u'
|
20
20
|
func = [@conv_to_full_posix_path, @conv_to_posix_path]
|
21
|
-
when
|
21
|
+
when 'w'
|
22
22
|
func = [@conv_to_full_win32_path, @conv_to_win32_path]
|
23
|
-
when
|
23
|
+
when 'a'
|
24
24
|
absolute = true
|
25
|
-
when
|
25
|
+
when 's'
|
26
26
|
shortname = true
|
27
27
|
end
|
28
|
-
|
29
|
-
raise
|
28
|
+
end
|
29
|
+
raise 'first argument must contain -u or -w' if func.nil?
|
30
|
+
|
30
31
|
func = absolute ? func[0] : func[1]
|
31
32
|
buf = "\0" * 300
|
32
|
-
if func.Call(path, buf) == -1
|
33
|
-
|
34
|
-
end
|
33
|
+
raise 'cannot convert path name' if func.Call(path, buf) == -1
|
34
|
+
|
35
35
|
buf.delete!("\0")
|
36
36
|
buf
|
37
37
|
end
|
@@ -3,17 +3,15 @@
|
|
3
3
|
require 'weakref'
|
4
4
|
require 'Win32API'
|
5
5
|
|
6
|
-
def ka
|
6
|
+
def ka
|
7
7
|
Excel.kill_all
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
module RobustExcelOle
|
10
|
+
module RobustExcelOle
|
12
11
|
|
13
12
|
# see https://docs.microsoft.com/en-us/office/vba/api/excel.application(object)#methods
|
14
13
|
|
15
|
-
class Excel < RangeOwners
|
16
|
-
|
14
|
+
class Excel < RangeOwners
|
17
15
|
attr_accessor :ole_excel
|
18
16
|
attr_accessor :created
|
19
17
|
attr_accessor :workbook
|
@@ -23,75 +21,75 @@ module RobustExcelOle
|
|
23
21
|
attr_reader :displayalerts
|
24
22
|
attr_reader :calculation
|
25
23
|
attr_reader :screenupdating
|
26
|
-
|
24
|
+
|
27
25
|
alias ole_object ole_excel
|
28
26
|
|
29
|
-
@@hwnd2excel = {}
|
27
|
+
@@hwnd2excel = {}
|
30
28
|
|
31
29
|
# creates a new Excel instance
|
32
30
|
# @param [Hash] options the options
|
33
|
-
# @option options [Variant] :displayalerts
|
31
|
+
# @option options [Variant] :displayalerts
|
34
32
|
# @option options [Boolean] :visible
|
35
33
|
# @option options [Symbol] :calculation
|
36
34
|
# @option options [Boolean] :screenupdating
|
37
35
|
# @return [Excel] a new Excel instance
|
38
36
|
def self.create(options = {})
|
39
|
-
new(options.merge(
|
37
|
+
new(options.merge(:reuse => false))
|
40
38
|
end
|
41
39
|
|
42
|
-
# connects to the current (first opened) Excel instance, if such a running Excel instance exists
|
40
|
+
# connects to the current (first opened) Excel instance, if such a running Excel instance exists
|
43
41
|
# returns a new Excel instance, otherwise
|
44
|
-
# @option options [Variant] :displayalerts
|
45
|
-
# @option options [Boolean] :visible
|
42
|
+
# @option options [Variant] :displayalerts
|
43
|
+
# @option options [Boolean] :visible
|
46
44
|
# @option options [Symbol] :calculation
|
47
45
|
# @option options [Boolean] :screenupdating
|
48
46
|
# @return [Excel] an Excel instance
|
49
47
|
def self.current(options = {})
|
50
|
-
new(options.merge(
|
48
|
+
new(options.merge(:reuse => true))
|
51
49
|
end
|
52
50
|
|
53
|
-
# returns an Excel instance
|
51
|
+
# returns an Excel instance
|
54
52
|
# @param [Win32Ole] (optional) a WIN32OLE object representing an Excel instance
|
55
53
|
# @param [Hash] options the options
|
56
|
-
# @option options [Boolean] :reuse
|
54
|
+
# @option options [Boolean] :reuse
|
57
55
|
# @option options [Boolean] :visible
|
58
|
-
# @option options [Variant] :displayalerts
|
59
|
-
# @option options [Boolean] :screenupdating
|
56
|
+
# @option options [Variant] :displayalerts
|
57
|
+
# @option options [Boolean] :screenupdating
|
60
58
|
# @option options [Symbol] :calculation
|
61
|
-
# options:
|
59
|
+
# options:
|
62
60
|
# :reuse connects to an already running Excel instance (true) or
|
63
61
|
# creates a new Excel instance (false) (default: true)
|
64
62
|
# :visible makes the Excel visible (default: false)
|
65
|
-
# :displayalerts enables or disables DisplayAlerts (true, false, :if_visible (default))
|
63
|
+
# :displayalerts enables or disables DisplayAlerts (true, false, :if_visible (default))
|
66
64
|
# :calculation calculation mode is being forced to be manual (:manual) or automatic (:automtic)
|
67
65
|
# or is not being forced (default: nil)
|
68
66
|
# :screenupdating turns on or off screen updating (default: true)
|
69
67
|
# @return [Excel] an Excel instance
|
70
|
-
def self.new(win32ole_excel = nil, options = {
|
68
|
+
def self.new(win32ole_excel = nil, options = {})
|
71
69
|
if win32ole_excel.is_a? Hash
|
72
70
|
options = win32ole_excel
|
73
71
|
win32ole_excel = nil
|
74
72
|
end
|
75
73
|
ole_xl = win32ole_excel unless win32ole_excel.nil?
|
76
|
-
options = {:reuse => true}.merge(options)
|
74
|
+
options = { :reuse => true }.merge(options)
|
77
75
|
ole_xl = current_excel if options[:reuse] == true
|
78
76
|
ole_xl ||= WIN32OLE.new('Excel.Application')
|
79
77
|
hwnd = ole_xl.HWnd
|
80
78
|
stored = hwnd2excel(hwnd)
|
81
|
-
if stored
|
79
|
+
if stored && stored.alive?
|
82
80
|
result = stored
|
83
|
-
else
|
81
|
+
else
|
84
82
|
result = super(options)
|
85
|
-
result.instance_variable_set(:@ole_excel, ole_xl)
|
83
|
+
result.instance_variable_set(:@ole_excel, ole_xl)
|
86
84
|
WIN32OLE.const_load(ole_xl, RobustExcelOle) unless RobustExcelOle.const_defined?(:CONSTANTS)
|
87
85
|
@@hwnd2excel[hwnd] = WeakRef.new(result)
|
88
86
|
end
|
89
|
-
|
87
|
+
|
90
88
|
unless options.is_a? WIN32OLE
|
91
89
|
begin
|
92
90
|
reused = options[:reuse] && stored && stored.alive?
|
93
91
|
unless reused
|
94
|
-
options = {:displayalerts => :if_visible, :visible => false, :screenupdating => true}.merge(options)
|
92
|
+
options = { :displayalerts => :if_visible, :visible => false, :screenupdating => true }.merge(options)
|
95
93
|
end
|
96
94
|
result.visible = options[:visible] unless options[:visible].nil?
|
97
95
|
result.displayalerts = options[:displayalerts] unless options[:displayalerts].nil?
|
@@ -99,12 +97,11 @@ module RobustExcelOle
|
|
99
97
|
result.screenupdating = options[:screenupdating] unless options[:screenupdating].nil?
|
100
98
|
result.created = !reused
|
101
99
|
end
|
102
|
-
end
|
100
|
+
end
|
103
101
|
result
|
104
102
|
end
|
105
103
|
|
106
|
-
def initialize(options= {})
|
107
|
-
end
|
104
|
+
def initialize(options = {}); end
|
108
105
|
|
109
106
|
# reopens a closed Excel instance
|
110
107
|
# @param [Hash] opts the options
|
@@ -115,72 +112,80 @@ module RobustExcelOle
|
|
115
112
|
# options: reopen_workbooks (default: false): reopen the workbooks in the Excel instances
|
116
113
|
# :visible (default: false), :displayalerts (default: :if_visible), :calculation (default: false)
|
117
114
|
# @return [Excel] an Excel instance
|
118
|
-
def recreate(opts = {})
|
119
|
-
unless
|
115
|
+
def recreate(opts = {})
|
116
|
+
unless alive?
|
120
117
|
opts = {
|
121
|
-
:visible => @visible
|
122
|
-
:displayalerts => @displayalerts
|
118
|
+
:visible => @visible || false,
|
119
|
+
:displayalerts => @displayalerts || :if_visible
|
123
120
|
}.merge(opts)
|
124
121
|
@ole_excel = WIN32OLE.new('Excel.Application')
|
125
122
|
self.visible = opts[:visible]
|
126
|
-
self.displayalerts = opts[:displayalerts]
|
123
|
+
self.displayalerts = opts[:displayalerts]
|
127
124
|
self.calculation = opts[:calculation]
|
128
125
|
if opts[:reopen_workbooks]
|
129
126
|
books = workbook_class.books
|
130
127
|
books.each do |book|
|
131
|
-
book.reopen if
|
132
|
-
end
|
128
|
+
book.reopen if !book.alive? && book.excel.alive? && book.excel == self
|
129
|
+
end
|
133
130
|
end
|
134
131
|
end
|
135
|
-
self
|
132
|
+
self
|
136
133
|
end
|
137
134
|
|
138
|
-
|
139
|
-
|
135
|
+
private
|
136
|
+
|
140
137
|
# returns a Win32OLE object that represents a Excel instance to which Excel connects
|
141
138
|
# connects to the first opened Excel instance
|
142
139
|
# if this Excel instance is being closed, then Excel creates a new Excel instance
|
143
|
-
def self.current_excel
|
144
|
-
result =
|
140
|
+
def self.current_excel # :nodoc: #
|
141
|
+
result = begin
|
142
|
+
WIN32OLE.connect('Excel.Application')
|
143
|
+
rescue
|
144
|
+
nil
|
145
|
+
end
|
145
146
|
if result
|
146
147
|
begin
|
147
|
-
result.Visible
|
148
|
-
rescue
|
149
|
-
trace
|
148
|
+
result.Visible # send any method, just to see if it responds
|
149
|
+
rescue
|
150
|
+
trace 'dead excel ' + (begin
|
151
|
+
"Window-handle = #{result.HWnd}"
|
152
|
+
rescue
|
153
|
+
'without window handle'
|
154
|
+
end)
|
150
155
|
return nil
|
151
156
|
end
|
152
157
|
end
|
153
158
|
result
|
154
159
|
end
|
155
160
|
|
156
|
-
|
161
|
+
public
|
157
162
|
|
158
163
|
# retain the saved status of all workbooks
|
159
164
|
def retain_saved_workbooks
|
160
165
|
saved = []
|
161
|
-
@ole_excel.Workbooks.each {|w| saved << w.Saved}
|
166
|
+
@ole_excel.Workbooks.each { |w| saved << w.Saved }
|
162
167
|
begin
|
163
168
|
yield self
|
164
169
|
ensure
|
165
170
|
i = 0
|
166
171
|
@ole_excel.Workbooks.each do |w|
|
167
172
|
w.Saved = saved[i] if saved[i]
|
168
|
-
i
|
173
|
+
i += 1
|
169
174
|
end
|
170
175
|
end
|
171
176
|
end
|
172
177
|
|
173
178
|
def self.contains_unsaved_workbooks?
|
174
|
-
|
179
|
+
!Excel.current.unsaved_workbooks.empty?
|
175
180
|
end
|
176
181
|
|
177
182
|
# returns unsaved workbooks (win32ole objects)
|
178
183
|
def unsaved_workbooks
|
179
|
-
unsaved_workbooks = []
|
180
|
-
begin
|
181
|
-
@ole_excel.Workbooks.each {|w| unsaved_workbooks << w unless
|
184
|
+
unsaved_workbooks = []
|
185
|
+
begin
|
186
|
+
@ole_excel.Workbooks.each { |w| unsaved_workbooks << w unless w.Saved || w.ReadOnly }
|
182
187
|
rescue RuntimeError => msg
|
183
|
-
raise ExcelDamaged,
|
188
|
+
raise ExcelDamaged, 'Excel instance not alive or damaged' if msg.message =~ /failed to get Dispatch Interface/
|
184
189
|
end
|
185
190
|
unsaved_workbooks
|
186
191
|
end
|
@@ -188,25 +193,26 @@ module RobustExcelOle
|
|
188
193
|
# closes workbooks
|
189
194
|
# @option options [Symbol] :if_unsaved :raise, :save, :forget, :alert, Proc
|
190
195
|
# :if_unsaved if unsaved workbooks are open in an Excel instance
|
191
|
-
# :raise (default) -> raises an exception
|
196
|
+
# :raise (default) -> raises an exception
|
192
197
|
# :save -> saves the workbooks before closing
|
193
|
-
# :forget -> closes the Excel instance without saving the workbooks
|
198
|
+
# :forget -> closes the Excel instance without saving the workbooks
|
194
199
|
# :alert -> let Excel do it
|
195
|
-
def close_workbooks(options = {:if_unsaved => :raise})
|
196
|
-
return
|
200
|
+
def close_workbooks(options = { :if_unsaved => :raise })
|
201
|
+
return unless alive?
|
202
|
+
|
197
203
|
weak_wkbks = @ole_excel.Workbooks
|
198
|
-
|
204
|
+
unless unsaved_workbooks.empty?
|
199
205
|
case options[:if_unsaved]
|
200
206
|
when Proc
|
201
207
|
options[:if_unsaved].call(self, unsaved_workbooks)
|
202
208
|
when :raise
|
203
|
-
raise UnsavedWorkbooks,
|
209
|
+
raise UnsavedWorkbooks, 'Excel contains unsaved workbooks'
|
204
210
|
when :alert
|
205
|
-
#nothing
|
211
|
+
# nothing
|
206
212
|
when :forget
|
207
|
-
unsaved_workbooks.each {|m| m.Saved = true}
|
213
|
+
unsaved_workbooks.each { |m| m.Saved = true }
|
208
214
|
when :save
|
209
|
-
unsaved_workbooks.each {|m| m.Save}
|
215
|
+
unsaved_workbooks.each { |m| m.Save }
|
210
216
|
else
|
211
217
|
raise OptionInvalid, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}"
|
212
218
|
end
|
@@ -215,11 +221,11 @@ module RobustExcelOle
|
|
215
221
|
@ole_excel.Workbooks.Close
|
216
222
|
rescue WIN32OLERuntimeError => msg
|
217
223
|
if msg.message =~ /800A03EC/
|
218
|
-
raise ExcelREOError,
|
219
|
-
else
|
224
|
+
raise ExcelREOError, 'user canceled or runtime error'
|
225
|
+
else
|
220
226
|
raise UnexpectedREOError, "unknown WIN32OLERuntimeError: #{msg.message}"
|
221
227
|
end
|
222
|
-
end
|
228
|
+
end
|
223
229
|
weak_wkbks = nil
|
224
230
|
weak_wkbks = @ole_excel.Workbooks
|
225
231
|
weak_wkbks = nil
|
@@ -233,12 +239,12 @@ module RobustExcelOle
|
|
233
239
|
# @option options [Symbol] :if_unsaved :raise, :save, :forget, or :alert
|
234
240
|
# options:
|
235
241
|
# :if_unsaved if unsaved workbooks are open in an Excel instance
|
236
|
-
# :raise (default) -> raises an exception
|
242
|
+
# :raise (default) -> raises an exception
|
237
243
|
# :save -> saves the workbooks before closing
|
238
|
-
# :forget -> closes the excel instance without saving the workbooks
|
244
|
+
# :forget -> closes the excel instance without saving the workbooks
|
239
245
|
# :alert -> give control to Excel
|
240
246
|
# @option options [Proc] block
|
241
|
-
def self.close_all(options = {:if_unsaved => :raise}, &blk)
|
247
|
+
def self.close_all(options = { :if_unsaved => :raise }, &blk)
|
242
248
|
options[:if_unsaved] = blk if blk
|
243
249
|
finished_number = error_number = overall_number = 0
|
244
250
|
first_error = nil
|
@@ -248,8 +254,8 @@ module RobustExcelOle
|
|
248
254
|
overall_number += 1
|
249
255
|
finished_number += excel.close(:if_unsaved => options[:if_unsaved])
|
250
256
|
rescue
|
251
|
-
first_error =
|
252
|
-
#trace "error when finishing #{$!}"
|
257
|
+
first_error = $ERROR_INFO
|
258
|
+
# trace "error when finishing #{$!}"
|
253
259
|
error_number += 1
|
254
260
|
end
|
255
261
|
end
|
@@ -264,22 +270,26 @@ module RobustExcelOle
|
|
264
270
|
finishing_action.call(excel)
|
265
271
|
end
|
266
272
|
else
|
267
|
-
@@hwnd2excel.delete(hwnd)
|
273
|
+
@@hwnd2excel.delete(hwnd)
|
268
274
|
end
|
269
275
|
end
|
270
276
|
|
271
277
|
# unknown Excel-instances
|
272
278
|
old_error_number = error_number
|
273
|
-
9.times do |
|
279
|
+
9.times do |_index|
|
274
280
|
sleep 0.1
|
275
|
-
excel =
|
281
|
+
excel = begin
|
282
|
+
new(WIN32OLE.connect('Excel.Application'))
|
283
|
+
rescue
|
284
|
+
nil
|
285
|
+
end
|
276
286
|
finishing_action.call(excel) if excel
|
277
|
-
free_all_ole_objects unless error_number > 0
|
278
|
-
break
|
279
|
-
break if error_number > old_error_number # + 3
|
287
|
+
free_all_ole_objects unless (error_number > 0) && (options[:if_unsaved] == :raise)
|
288
|
+
break unless excel
|
289
|
+
break if error_number > old_error_number # + 3
|
280
290
|
end
|
281
291
|
|
282
|
-
raise first_error if (options[:if_unsaved] == :raise
|
292
|
+
raise first_error if ((options[:if_unsaved] == :raise) && first_error) || (first_error.class == OptionInvalid)
|
283
293
|
|
284
294
|
[finished_number, error_number]
|
285
295
|
end
|
@@ -287,47 +297,51 @@ module RobustExcelOle
|
|
287
297
|
# closes the Excel
|
288
298
|
# @param [Hash] options the options
|
289
299
|
# @option options [Symbol] :if_unsaved :raise, :save, :forget, :alert
|
290
|
-
# @option options [Boolean] :hard
|
300
|
+
# @option options [Boolean] :hard
|
291
301
|
# :if_unsaved if unsaved workbooks are open in an Excel instance
|
292
|
-
# :raise (default) -> raises an exception
|
302
|
+
# :raise (default) -> raises an exception
|
293
303
|
# :save -> saves the workbooks before closing
|
294
|
-
# :forget -> closes the Excel instance without saving the workbooks
|
295
|
-
# :alert -> Excel takes over
|
296
|
-
def close(options = {:if_unsaved => :raise})
|
297
|
-
finishing_living_excel =
|
298
|
-
if finishing_living_excel
|
299
|
-
hwnd = (
|
304
|
+
# :forget -> closes the Excel instance without saving the workbooks
|
305
|
+
# :alert -> Excel takes over
|
306
|
+
def close(options = { :if_unsaved => :raise })
|
307
|
+
finishing_living_excel = alive?
|
308
|
+
if finishing_living_excel
|
309
|
+
hwnd = (begin
|
310
|
+
@ole_excel.HWnd
|
311
|
+
rescue
|
312
|
+
nil
|
313
|
+
end)
|
300
314
|
close_workbooks(:if_unsaved => options[:if_unsaved])
|
301
315
|
@ole_excel.Quit
|
302
|
-
if false
|
316
|
+
if false && defined?(weak_wkbks) && weak_wkbks.weakref_alive?
|
303
317
|
weak_wkbks.ole_free
|
304
318
|
end
|
305
319
|
weak_xl = WeakRef.new(@ole_excel)
|
306
320
|
else
|
307
321
|
weak_xl = nil
|
308
|
-
end
|
322
|
+
end
|
309
323
|
@ole_excel = nil
|
310
324
|
GC.start
|
311
325
|
sleep 0.1
|
312
|
-
if finishing_living_excel
|
313
|
-
if hwnd
|
314
|
-
process_id = Win32API.new(
|
315
|
-
pid_puffer =
|
326
|
+
if finishing_living_excel
|
327
|
+
if hwnd
|
328
|
+
process_id = Win32API.new('user32', 'GetWindowThreadProcessId', %w[I P], 'I')
|
329
|
+
pid_puffer = ' ' * 32
|
316
330
|
process_id.call(hwnd, pid_puffer)
|
317
|
-
pid = pid_puffer.unpack(
|
331
|
+
pid = pid_puffer.unpack('L')[0]
|
318
332
|
begin
|
319
|
-
Process.kill(
|
320
|
-
rescue
|
321
|
-
#trace "kill_error: #{$!}"
|
333
|
+
Process.kill('KILL', pid)
|
334
|
+
rescue
|
335
|
+
# trace "kill_error: #{$!}"
|
322
336
|
end
|
323
337
|
end
|
324
338
|
@@hwnd2excel.delete(hwnd)
|
325
|
-
if weak_xl.weakref_alive?
|
326
|
-
|
339
|
+
if weak_xl.weakref_alive?
|
340
|
+
# if WIN32OLE.ole_reference_count(weak_xlapp) > 0
|
327
341
|
begin
|
328
342
|
weak_xl.ole_free
|
329
343
|
rescue
|
330
|
-
#trace "weakref_probl_olefree"
|
344
|
+
# trace "weakref_probl_olefree"
|
331
345
|
end
|
332
346
|
end
|
333
347
|
end
|
@@ -335,43 +349,42 @@ module RobustExcelOle
|
|
335
349
|
end
|
336
350
|
|
337
351
|
# frees all OLE objects in the object space
|
338
|
-
def self.free_all_ole_objects
|
352
|
+
def self.free_all_ole_objects # :nodoc: #
|
339
353
|
anz_objekte = 0
|
340
|
-
ObjectSpace.each_object(WIN32OLE) do |o|
|
354
|
+
ObjectSpace.each_object(WIN32OLE) do |o|
|
341
355
|
anz_objekte += 1
|
342
|
-
#trace "#{anz_objekte} name: #{(o.Name rescue (o.Count rescue "no_name"))} ole_object_name: #{(o.ole_object_name rescue nil)} type: #{o.ole_type rescue nil}"
|
343
|
-
#trace [:Name, (o.Name rescue (o.Count rescue "no_name"))]
|
344
|
-
#trace [:ole_object_name, (o.ole_object_name rescue nil)]
|
345
|
-
#trace [:methods, (o.ole_methods rescue nil)] unless (o.Name rescue false)
|
346
|
-
#trace o.ole_type rescue nil
|
356
|
+
# trace "#{anz_objekte} name: #{(o.Name rescue (o.Count rescue "no_name"))} ole_object_name: #{(o.ole_object_name rescue nil)} type: #{o.ole_type rescue nil}"
|
357
|
+
# trace [:Name, (o.Name rescue (o.Count rescue "no_name"))]
|
358
|
+
# trace [:ole_object_name, (o.ole_object_name rescue nil)]
|
359
|
+
# trace [:methods, (o.ole_methods rescue nil)] unless (o.Name rescue false)
|
360
|
+
# trace o.ole_type rescue nil
|
347
361
|
begin
|
348
362
|
o.ole_free
|
349
|
-
#trace "olefree OK"
|
363
|
+
# trace "olefree OK"
|
350
364
|
rescue
|
351
|
-
#trace "olefree_error: #{$!}"
|
352
|
-
#trace $!.backtrace.first(9).join "\n"
|
365
|
+
# trace "olefree_error: #{$!}"
|
366
|
+
# trace $!.backtrace.first(9).join "\n"
|
353
367
|
end
|
354
368
|
end
|
355
|
-
#trace "went through #{anz_objekte} OLE objects"
|
356
|
-
end
|
357
|
-
|
369
|
+
# trace "went through #{anz_objekte} OLE objects"
|
370
|
+
end
|
358
371
|
|
359
372
|
def self.init
|
360
373
|
@@hwnd2excel = {}
|
361
|
-
end
|
374
|
+
end
|
362
375
|
|
363
376
|
# kill all Excel instances
|
364
377
|
# @return [Integer] number of killed Excel processes
|
365
378
|
def self.kill_all
|
366
379
|
number = 0
|
367
|
-
WIN32OLE.connect(
|
380
|
+
WIN32OLE.connect('winmgmts:\\\\.').InstancesOf('win32_process').each do |p|
|
368
381
|
begin
|
369
|
-
if p.name ==
|
370
|
-
Process.kill('KILL', p.processid)
|
382
|
+
if p.name == 'EXCEL.EXE'
|
383
|
+
Process.kill('KILL', p.processid)
|
371
384
|
number += 1
|
372
385
|
end
|
373
|
-
rescue
|
374
|
-
|
386
|
+
rescue
|
387
|
+
# trace "kill error: #{$!}"
|
375
388
|
end
|
376
389
|
end
|
377
390
|
init
|
@@ -379,53 +392,53 @@ module RobustExcelOle
|
|
379
392
|
end
|
380
393
|
|
381
394
|
def self.excels_number
|
382
|
-
processes = WIN32OLE.connect(
|
383
|
-
processes.select{ |p| p.name ==
|
395
|
+
processes = WIN32OLE.connect('winmgmts:\\\\.').InstancesOf('win32_process')
|
396
|
+
processes.select { |p| p.name == 'EXCEL.EXE' }.size
|
384
397
|
end
|
385
398
|
|
386
399
|
# returns all Excel objects for all Excel instances opened with RobustExcelOle,
|
387
400
|
def self.known_excel_instances
|
388
401
|
pid2excel = {}
|
389
402
|
@@hwnd2excel.each do |hwnd,wr_excel|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
403
|
+
next unless wr_excel.weakref_alive?
|
404
|
+
|
405
|
+
excel = wr_excel.__getobj__
|
406
|
+
process_id = Win32API.new('user32', 'GetWindowThreadProcessId', %w[I P], 'I')
|
407
|
+
pid_puffer = ' ' * 32
|
408
|
+
process_id.call(hwnd, pid_puffer)
|
409
|
+
pid = pid_puffer.unpack('L')[0]
|
410
|
+
pid2excel[pid] = excel
|
398
411
|
end
|
399
|
-
processes = WIN32OLE.connect(
|
400
|
-
#excel_processes = processes.select{ |p| p.name == "EXCEL.EXE" && pid2excel.include?(p.processid)}
|
401
|
-
#excel_processes.map{ |p| Excel.new(pid2excel[p.processid]) }
|
402
|
-
processes.select{ |p| Excel.new(pid2excel[p.processid]) if p.name ==
|
412
|
+
processes = WIN32OLE.connect('winmgmts:\\\\.').InstancesOf('win32_process')
|
413
|
+
# excel_processes = processes.select{ |p| p.name == "EXCEL.EXE" && pid2excel.include?(p.processid)}
|
414
|
+
# excel_processes.map{ |p| Excel.new(pid2excel[p.processid]) }
|
415
|
+
processes.select { |p| Excel.new(pid2excel[p.processid]) if p.name == 'EXCEL.EXE' && pid2excel.include?(p.processid) }
|
403
416
|
result = []
|
404
417
|
processes.each do |p|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
# how to connect to an (interactively opened) Excel instance and get a WIN32OLE object?
|
411
|
-
# after that, lift it to an Excel object
|
418
|
+
next unless p.name == 'EXCEL.EXE'
|
419
|
+
|
420
|
+
if pid2excel.include?(p.processid)
|
421
|
+
excel = pid2excel[p.processid]
|
422
|
+
result << excel
|
412
423
|
end
|
424
|
+
# how to connect to an (interactively opened) Excel instance and get a WIN32OLE object?
|
425
|
+
# after that, lift it to an Excel object
|
413
426
|
end
|
414
427
|
result
|
415
428
|
end
|
416
429
|
|
417
|
-
def excel
|
430
|
+
def excel # :nodoc: #
|
418
431
|
self
|
419
432
|
end
|
420
433
|
|
421
|
-
def self.hwnd2excel(hwnd)
|
434
|
+
def self.hwnd2excel(hwnd) # :nodoc: #
|
422
435
|
excel_weakref = @@hwnd2excel[hwnd]
|
423
436
|
if excel_weakref
|
424
437
|
if excel_weakref.weakref_alive?
|
425
438
|
excel_weakref.__getobj__
|
426
439
|
else
|
427
|
-
trace
|
428
|
-
begin
|
440
|
+
trace 'dead reference to an Excel'
|
441
|
+
begin
|
429
442
|
@@hwnd2excel.delete(hwnd)
|
430
443
|
nil
|
431
444
|
rescue
|
@@ -435,13 +448,15 @@ module RobustExcelOle
|
|
435
448
|
end
|
436
449
|
end
|
437
450
|
|
438
|
-
def hwnd
|
439
|
-
self.Hwnd
|
451
|
+
def hwnd # :nodoc: #
|
452
|
+
self.Hwnd
|
453
|
+
rescue
|
454
|
+
nil
|
440
455
|
end
|
441
456
|
|
442
|
-
def self.print_hwnd2excel
|
457
|
+
def self.print_hwnd2excel # :nodoc: #
|
443
458
|
@@hwnd2excel.each do |hwnd,wr_excel|
|
444
|
-
excel_string = (wr_excel.weakref_alive? ? wr_excel.__getobj__.to_s :
|
459
|
+
excel_string = (wr_excel.weakref_alive? ? wr_excel.__getobj__.to_s : 'weakref not alive')
|
445
460
|
printf("hwnd: %8i => excel: %s\n", hwnd, excel_string)
|
446
461
|
end
|
447
462
|
@@hwnd2excel.size
|
@@ -449,7 +464,7 @@ module RobustExcelOle
|
|
449
464
|
|
450
465
|
# returns true, if the Excel instances are alive and identical, false otherwise
|
451
466
|
def == other_excel
|
452
|
-
self.Hwnd == other_excel.Hwnd
|
467
|
+
self.Hwnd == other_excel.Hwnd if other_excel.is_a?(Excel) && alive? && other_excel.alive?
|
453
468
|
end
|
454
469
|
|
455
470
|
# returns true, if the Excel instances responds to VBA methods, false otherwise
|
@@ -457,67 +472,67 @@ module RobustExcelOle
|
|
457
472
|
@ole_excel.Name
|
458
473
|
true
|
459
474
|
rescue
|
460
|
-
#trace $!.message
|
475
|
+
# trace $!.message
|
461
476
|
false
|
462
477
|
end
|
463
478
|
|
464
|
-
|
465
479
|
# returns unsaved workbooks in known (not opened by user) Excel instances
|
466
|
-
def self.unsaved_known_workbooks
|
480
|
+
def self.unsaved_known_workbooks # :nodoc: #
|
467
481
|
result = []
|
468
|
-
@@hwnd2excel.each do |
|
482
|
+
@@hwnd2excel.each do |_hwnd,wr_excel|
|
469
483
|
excel = wr_excel.__getobj__ if wr_excel.weakref_alive?
|
470
484
|
result << excel.unsaved_workbooks
|
471
485
|
end
|
472
486
|
result
|
473
487
|
end
|
474
488
|
|
475
|
-
def print_workbooks
|
476
|
-
self.Workbooks.each {|w| trace "#{w.Name} #{w}"}
|
489
|
+
def print_workbooks # :nodoc: #
|
490
|
+
self.Workbooks.each { |w| trace "#{w.Name} #{w}" }
|
477
491
|
end
|
478
492
|
|
479
493
|
# generates, saves, and closes empty workbook
|
480
|
-
def generate_workbook file_name
|
481
|
-
raise FileNameNotGiven,
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
494
|
+
def generate_workbook file_name # :nodoc: #
|
495
|
+
raise FileNameNotGiven, 'filename is nil' if file_name.nil?
|
496
|
+
|
497
|
+
self.Workbooks.Add
|
498
|
+
empty_workbook = self.Workbooks.Item(self.Workbooks.Count)
|
499
|
+
filename = General.absolute_path(file_name).tr('/','\\')
|
500
|
+
unless File.exist?(filename)
|
486
501
|
begin
|
487
|
-
empty_workbook.SaveAs(filename)
|
502
|
+
empty_workbook.SaveAs(filename)
|
488
503
|
rescue WIN32OLERuntimeError => msg
|
489
|
-
#if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
|
490
|
-
|
491
|
-
#else
|
492
|
-
# # todo some time: find out when this occurs :
|
504
|
+
# if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
|
505
|
+
raise FileNotFound, "could not save workbook with filename #{file_name.inspect}"
|
506
|
+
# else
|
507
|
+
# # todo some time: find out when this occurs :
|
493
508
|
# raise UnexpectedREOError, "unknown WIN32OLERuntimeError with filename #{file_name.inspect}: \n#{msg.message}"
|
494
|
-
#end
|
495
|
-
end
|
509
|
+
# end
|
510
|
+
end
|
496
511
|
end
|
497
|
-
empty_workbook
|
512
|
+
empty_workbook
|
498
513
|
end
|
499
514
|
|
500
515
|
# sets DisplayAlerts in a block
|
501
516
|
def with_displayalerts displayalerts_value
|
502
|
-
old_displayalerts =
|
517
|
+
old_displayalerts = displayalerts
|
503
518
|
self.displayalerts = displayalerts_value
|
504
519
|
begin
|
505
|
-
|
520
|
+
yield self
|
506
521
|
ensure
|
507
522
|
self.displayalerts = old_displayalerts if alive?
|
508
523
|
end
|
509
|
-
end
|
524
|
+
end
|
510
525
|
|
511
526
|
# makes the current Excel instance visible or invisible
|
512
527
|
def visible= visible_value
|
513
528
|
@ole_excel.Visible = @visible = visible_value
|
514
529
|
@ole_excel.DisplayAlerts = @visible if @displayalerts == :if_visible
|
515
|
-
end
|
530
|
+
end
|
516
531
|
|
517
532
|
# enables DisplayAlerts in the current Excel instance
|
518
533
|
def displayalerts= displayalerts_value
|
519
534
|
@displayalerts = displayalerts_value
|
520
|
-
@ole_excel.DisplayAlerts =
|
535
|
+
@ole_excel.DisplayAlerts = @displayalerts == :if_visible ? @ole_excel.Visible : displayalerts_value
|
521
536
|
end
|
522
537
|
|
523
538
|
# sets ScreenUpdating
|
@@ -529,20 +544,21 @@ module RobustExcelOle
|
|
529
544
|
# retains the saved-status of the workbooks when set to manual
|
530
545
|
def calculation= calculation_mode
|
531
546
|
return if calculation_mode.nil?
|
547
|
+
|
532
548
|
@calculation = calculation_mode
|
533
|
-
calc_mode_changable = @ole_excel.Workbooks.Count > 0 &&
|
549
|
+
calc_mode_changable = @ole_excel.Workbooks.Count > 0 && @ole_excel.Calculation.is_a?(Integer)
|
534
550
|
if calc_mode_changable
|
535
|
-
if calculation_mode == :manual
|
551
|
+
if calculation_mode == :manual
|
536
552
|
saved = []
|
537
|
-
(1..@ole_excel.Workbooks.Count).each {|i| saved << @ole_excel.Workbooks(i).Saved}
|
553
|
+
(1..@ole_excel.Workbooks.Count).each { |i| saved << @ole_excel.Workbooks(i).Saved }
|
538
554
|
end
|
539
555
|
@ole_excel.CalculateBeforeSave = false
|
540
|
-
@ole_excel.Calculation =
|
541
|
-
|
542
|
-
if calculation_mode == :manual
|
543
|
-
(1..@ole_excel.Workbooks.Count).each {|i| @ole_excel.Workbooks(i).Saved = true if saved[i-1]}
|
556
|
+
@ole_excel.Calculation =
|
557
|
+
calculation_mode == :automatic ? XlCalculationAutomatic : XlCalculationManual
|
558
|
+
if calculation_mode == :manual
|
559
|
+
(1..@ole_excel.Workbooks.Count).each { |i| @ole_excel.Workbooks(i).Saved = true if saved[i - 1] }
|
544
560
|
end
|
545
|
-
end
|
561
|
+
end
|
546
562
|
end
|
547
563
|
|
548
564
|
# VBA method overwritten
|
@@ -559,6 +575,7 @@ module RobustExcelOle
|
|
559
575
|
# sets calculation mode in a block
|
560
576
|
def with_calculation(calculation_mode)
|
561
577
|
return unless calculation_mode
|
578
|
+
|
562
579
|
old_calculation_mode = @ole_excel.Calculation
|
563
580
|
begin
|
564
581
|
self.calculation = calculation_mode
|
@@ -583,10 +600,10 @@ module RobustExcelOle
|
|
583
600
|
@ole_excel.Workbooks
|
584
601
|
rescue WIN32OLERuntimeError => msg
|
585
602
|
if msg.message =~ /failed to get Dispatch Interface/
|
586
|
-
raise ExcelDamaged,
|
603
|
+
raise ExcelDamaged, 'Excel instance not alive or damaged'
|
587
604
|
else
|
588
|
-
raise ExcelREOError,
|
589
|
-
end
|
605
|
+
raise ExcelREOError, 'workbooks could not be determined'
|
606
|
+
end
|
590
607
|
end
|
591
608
|
ole_workbooks.each do |ole_workbook|
|
592
609
|
workbook_class.open(ole_workbook).for_this_workbook(options)
|
@@ -595,11 +612,11 @@ module RobustExcelOle
|
|
595
612
|
|
596
613
|
def focus
|
597
614
|
self.visible = true
|
598
|
-
#if not Windows10 then
|
599
|
-
Win32API.new(
|
600
|
-
#else
|
601
|
-
#Win32API.new("user32","SetForegroundWindow","","I").call
|
602
|
-
#end
|
615
|
+
# if not Windows10 then
|
616
|
+
Win32API.new('user32','SetForegroundWindow','I','I').call(@ole_excel.Hwnd)
|
617
|
+
# else
|
618
|
+
# Win32API.new("user32","SetForegroundWindow","","I").call
|
619
|
+
# end
|
603
620
|
end
|
604
621
|
|
605
622
|
# returns the value of a range
|
@@ -612,21 +629,21 @@ module RobustExcelOle
|
|
612
629
|
# sets the value of a range
|
613
630
|
# @param [String] name the name of the range
|
614
631
|
# @param [Variant] value the contents of the range
|
615
|
-
def []=
|
632
|
+
def []=(name, value)
|
616
633
|
set_namevalue_glob(name,value, :color => 42) # 42 - aqua-marin, 7-green
|
617
634
|
end
|
618
635
|
|
619
636
|
def to_s # :nodoc: #
|
620
|
-
|
637
|
+
'#<Excel: ' + hwnd.to_s + ('not alive' unless alive?).to_s + '>'
|
621
638
|
end
|
622
639
|
|
623
640
|
def inspect # :nodoc: #
|
624
|
-
|
641
|
+
to_s
|
625
642
|
end
|
626
643
|
|
627
644
|
def self.workbook_class # :nodoc: #
|
628
645
|
@workbook_class ||= begin
|
629
|
-
module_name =
|
646
|
+
module_name = parent_name
|
630
647
|
"#{module_name}::Workbook".constantize
|
631
648
|
rescue NameError => e
|
632
649
|
Workbook
|
@@ -639,30 +656,30 @@ module RobustExcelOle
|
|
639
656
|
|
640
657
|
include MethodHelpers
|
641
658
|
|
642
|
-
|
659
|
+
private
|
660
|
+
|
661
|
+
def method_missing(name, *args) # :nodoc: #
|
662
|
+
if name.to_s[0,1] =~ /[A-Z]/
|
663
|
+
begin
|
664
|
+
raise ObjectNotAlive, 'method missing: Excel not alive' unless alive?
|
643
665
|
|
644
|
-
def method_missing(name, *args) # :nodoc: #
|
645
|
-
if name.to_s[0,1] =~ /[A-Z]/
|
646
|
-
begin
|
647
|
-
raise ObjectNotAlive, "method missing: Excel not alive" unless alive?
|
648
666
|
@ole_excel.send(name, *args)
|
649
667
|
rescue WIN32OLERuntimeError => msg
|
650
668
|
if msg.message =~ /unknown property or method/
|
651
669
|
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
652
|
-
else
|
670
|
+
else
|
653
671
|
raise msg
|
654
672
|
end
|
655
673
|
end
|
656
|
-
else
|
657
|
-
super
|
674
|
+
else
|
675
|
+
super
|
658
676
|
end
|
659
677
|
end
|
660
|
-
|
661
678
|
end
|
662
679
|
|
663
680
|
public
|
664
681
|
|
665
|
-
|
682
|
+
Application = Excel
|
666
683
|
|
667
684
|
end
|
668
685
|
|