com 0.3.0
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/README +225 -0
- data/Rakefile +10 -0
- data/lib/com.rb +73 -0
- data/lib/com/error.rb +44 -0
- data/lib/com/events.rb +68 -0
- data/lib/com/instantiable.rb +135 -0
- data/lib/com/methodinvocationerror.rb +38 -0
- data/lib/com/object.rb +79 -0
- data/lib/com/pathname.rb +10 -0
- data/lib/com/patternerror.rb +20 -0
- data/lib/com/standarderror.rb +61 -0
- data/lib/com/version.rb +5 -0
- data/lib/com/win32ole.rb +19 -0
- data/lib/com/wrapper.rb +96 -0
- data/test/unit/com.rb +23 -0
- data/test/unit/com/error.rb +11 -0
- data/test/unit/com/events.rb +28 -0
- data/test/unit/com/instantiable.rb +94 -0
- data/test/unit/com/methodinvocationerror.rb +67 -0
- data/test/unit/com/object.rb +33 -0
- data/test/unit/com/pathname.rb +21 -0
- data/test/unit/com/standarderror.rb +27 -0
- metadata +178 -0
data/README
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
# COM #
|
2
|
+
|
3
|
+
COM is an object-oriented wrapper around WIN32OLE. COM makes it easy to add
|
4
|
+
behavior to WIN32OLE objects, making them easier to work with from Ruby.
|
5
|
+
|
6
|
+
|
7
|
+
## Usage ##
|
8
|
+
|
9
|
+
Using COM is rather straightforward. There’s basically four concepts to keep
|
10
|
+
track of:
|
11
|
+
|
12
|
+
1. COM objects
|
13
|
+
2. Instantiable COM objects
|
14
|
+
3. COM events
|
15
|
+
4. COM errors
|
16
|
+
|
17
|
+
Let’s look at each concept separately, using the following example as a base.
|
18
|
+
|
19
|
+
module Word end
|
20
|
+
|
21
|
+
class Word::Application < COM::Instantiable
|
22
|
+
def without_interaction
|
23
|
+
with_properties('displayalerts' => Word::WdAlertsNone){ yield }
|
24
|
+
end
|
25
|
+
|
26
|
+
def documents
|
27
|
+
Word::Documents.new(com.documents)
|
28
|
+
end
|
29
|
+
|
30
|
+
def quit(saving = Word::WdDoNotSaveChanges, *args)
|
31
|
+
com.quit saving, *args
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
### COM Objects ###
|
36
|
+
|
37
|
+
A COM::Object is a wrapper around a COM object. It provides error
|
38
|
+
specialization, which is discussed later and a few utility methods. You
|
39
|
+
typically use it to wrap COM objects that are returned by COM methods. If we
|
40
|
+
take the example given in the introduction, Word::Documents is a good
|
41
|
+
candidate:
|
42
|
+
|
43
|
+
class Word::Documents < COM::Object
|
44
|
+
DefaultOpenOptions = {
|
45
|
+
'confirmconversions' => false,
|
46
|
+
'readonly' => true,
|
47
|
+
'addtorecentfiles' => false,
|
48
|
+
'visible' => false
|
49
|
+
}.freeze
|
50
|
+
def open(path, options = {})
|
51
|
+
options = DefaultOpenOptions.merge(options)
|
52
|
+
options['filename'] = Pathname(path).to_com
|
53
|
+
Word::Document.new(com.open(options))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
Here we override the #open method to be a bit easier to use, providing sane
|
58
|
+
defaults for COM interaction. Worth noting is the use of the #com method to
|
59
|
+
access the actual COM object to invoke the #open method on it. Also note that
|
60
|
+
Word::Document is also a COM::Object.
|
61
|
+
|
62
|
+
COM::Object provides a convenience method called #with_properties, which is
|
63
|
+
used in the #without_interaction method above. It lets you set properties on
|
64
|
+
the COM::Object during the duration of a block, restoring them after it exits
|
65
|
+
(successfully or with an error).
|
66
|
+
|
67
|
+
|
68
|
+
### Instantiable COM Objects ###
|
69
|
+
|
70
|
+
Instantiable COM objects are COM objects that we can connect to and that can be
|
71
|
+
created. The Word::Application object can, for example, be created.
|
72
|
+
Instantiable COM objects should inherit from COM::Instantiable. Instantiable
|
73
|
+
COM objects can be told what program ID to use, whether or not to allow
|
74
|
+
connecting to an already running object, and to load its associated constants
|
75
|
+
upon creation.
|
76
|
+
|
77
|
+
The program ID is used to determine what instantiable COM object to connect to.
|
78
|
+
By default the name of the COM::Instantiable class’ name is used, taking the
|
79
|
+
last two double-colon-separated components and joining them with a dot. For
|
80
|
+
Word::Application, the program ID is “Word.Application”. The program ID can be
|
81
|
+
set by using the .program_id method:
|
82
|
+
|
83
|
+
class IDontCare::ForConventions < COM::Instantiable
|
84
|
+
program_id 'Word.Application'
|
85
|
+
end
|
86
|
+
|
87
|
+
The program ID can be accessed with the same method:
|
88
|
+
|
89
|
+
Word::Application.program_id # ⇒ 'Word.Application'
|
90
|
+
|
91
|
+
Connecting to an already running COM object is not done by default, but is
|
92
|
+
sometimes desirable: the COM object might take a long time to create, or some
|
93
|
+
common state needs to be accessed. If the default for a certain instantiable
|
94
|
+
COM object should be to connect, this can be done using the .connect method:
|
95
|
+
|
96
|
+
class Word::Application < COM::Instantiable
|
97
|
+
connect
|
98
|
+
end
|
99
|
+
|
100
|
+
If no running COM object is available, then a new COM object will be created in
|
101
|
+
its stead. Whether or not a class uses the connection method can be queried
|
102
|
+
with the .connect? method:
|
103
|
+
|
104
|
+
Word::Application.connect? # ⇒ true
|
105
|
+
|
106
|
+
Whether or not to load constants associated with an instantiable COM object is
|
107
|
+
set with the .constants method:
|
108
|
+
|
109
|
+
class Word::Application < COM::Instantiable
|
110
|
+
constants true
|
111
|
+
end
|
112
|
+
|
113
|
+
and can similarly be checked:
|
114
|
+
|
115
|
+
Word::Application.constants? # ⇒ true
|
116
|
+
|
117
|
+
Constants are loaded by default.
|
118
|
+
|
119
|
+
When an instance of the instantiable COM object is created, a check is run to
|
120
|
+
see if constants should be loaded and whether or not they already have been
|
121
|
+
loaded. If they should be loaded and they haven’t already been loaded,
|
122
|
+
they’re, you guessed it, loaded. The constants are added to the module
|
123
|
+
containing the COM::Instantiable. Thus, for Word::Application, the Word module
|
124
|
+
will contain all the constants. Whether or not the constants have already been
|
125
|
+
loaded can be checked with .constants_loaded?:
|
126
|
+
|
127
|
+
Word::Application.constants_loaded # ⇒ false
|
128
|
+
|
129
|
+
That concludes the class-level methods.
|
130
|
+
|
131
|
+
Let’s begin with the #connected? method among the instance-level methods. This
|
132
|
+
method queries whether or not this instance connected to an already running COM
|
133
|
+
object:
|
134
|
+
|
135
|
+
Word::Application.new.connected? # ⇒ false
|
136
|
+
|
137
|
+
This can be very important in determining how shutdown of a COM object should
|
138
|
+
be done. If you connected to an already COM object it might be foolish to shut
|
139
|
+
it down if someone else is using it.
|
140
|
+
|
141
|
+
The #initialize method takes a couple of options:
|
142
|
+
|
143
|
+
* connect: whether or not to connect to a running instance
|
144
|
+
* constants: whether or not to load constants
|
145
|
+
|
146
|
+
These options will, when given, override the class-level defaults.
|
147
|
+
|
148
|
+
### Events ###
|
149
|
+
|
150
|
+
COM events are easily dealt with:
|
151
|
+
|
152
|
+
class Word::Application < COM::Instantiable
|
153
|
+
def initialize(options = {})
|
154
|
+
super
|
155
|
+
@events = COM::Events.new(com, 'ApplicationEvents',
|
156
|
+
'OnQuit')
|
157
|
+
end
|
158
|
+
|
159
|
+
def quit(saving = Word::WdDoNotSaveChanges, *args)
|
160
|
+
@events.observe('OnQuit', proc{ com.quit saving, *args }) do
|
161
|
+
yield if block_given?
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
To tell you the truth this API sucks and will most likely be rewritten. The
|
167
|
+
reason that it is the way it is is that WIN32OLE, which COM wraps, sucks. It’s
|
168
|
+
event API is horrid and the implementation is buggy. It will keep every
|
169
|
+
registered event block in memory for ever, freeing neither the blocks nor the
|
170
|
+
COM objects that yield the events.
|
171
|
+
|
172
|
+
### Errors ###
|
173
|
+
|
174
|
+
All errors generated by COM methods descend from COM::Error, except for those
|
175
|
+
cases where a Ruby error already exists. The following HRESULT error codes are
|
176
|
+
turned into Ruby errors:
|
177
|
+
|
178
|
+
HRESULT Error Code | Error Class
|
179
|
+
-------------------|------------
|
180
|
+
0x80004001 | NotImplementedError
|
181
|
+
0x80020005 | TypeError
|
182
|
+
0x80020006 | NoMethodError
|
183
|
+
0x8002000e | ArgumentError
|
184
|
+
0x800401e4 | ArgumentError
|
185
|
+
|
186
|
+
There are also a couple of other HRESULT error codes that are turned into more
|
187
|
+
specific errors than COM::Error:
|
188
|
+
|
189
|
+
HRESULT Error Code | Error Class
|
190
|
+
-------------------|------------
|
191
|
+
0x80020003 | MemberNotFoundError
|
192
|
+
0x800401e3 | OperationUnavailableError
|
193
|
+
|
194
|
+
Finally, when a method results in any other error, a COM::MethodInvocationError
|
195
|
+
will be raised, which can be queried for the specifics, specifically #message,
|
196
|
+
#method, #server, #code, #hresult_code, and #hresult_message.
|
197
|
+
|
198
|
+
### Pathname ###
|
199
|
+
|
200
|
+
The Pathname object receives an additional method, #to_com. This method is
|
201
|
+
useful for when you want to pass a Pathname object to a COM method. Simply
|
202
|
+
call #to_com to turn it into a String of the right encoding for COM:
|
203
|
+
|
204
|
+
Word::Application.new.documents.open(Pathname('a.docx').to_com)
|
205
|
+
# ⇒ Word::Document
|
206
|
+
|
207
|
+
|
208
|
+
## Installation ##
|
209
|
+
|
210
|
+
Install COM with
|
211
|
+
|
212
|
+
% gem install com
|
213
|
+
|
214
|
+
|
215
|
+
## License ##
|
216
|
+
|
217
|
+
You may use, copy and redistribute this library under the same [terms][1] as
|
218
|
+
Ruby itself.
|
219
|
+
|
220
|
+
[1]: http://www.ruby-lang.org/en/LICENSE.txt
|
221
|
+
|
222
|
+
|
223
|
+
## Contributors ##
|
224
|
+
|
225
|
+
* Nikolai Weibull
|
data/Rakefile
ADDED
data/lib/com.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'win32ole'
|
4
|
+
WIN32OLE.codepage = WIN32OLE::CP_UTF8
|
5
|
+
|
6
|
+
# COM is an object-oriented wrapper around WIN32OLE. COM makes it easy to add
|
7
|
+
# behavior to WIN32OLE objects, making them easier to work with from Ruby.
|
8
|
+
module COM
|
9
|
+
autoload :Error, 'com/error'
|
10
|
+
autoload :Events, 'com/events'
|
11
|
+
autoload :Instantiable, 'com/instantiable'
|
12
|
+
autoload :Object, 'com/object'
|
13
|
+
autoload :PatternError, 'com/patternerror'
|
14
|
+
autoload :Version, 'com/version'
|
15
|
+
autoload :Wrapper, 'com/wrapper'
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Gets the iconv character set equivalent of the current COM code page.
|
19
|
+
#
|
20
|
+
# @raise [RuntimeError] If no iconv charset is associated with the current
|
21
|
+
# COM codepage
|
22
|
+
# @return [String] The iconv character set
|
23
|
+
def charset
|
24
|
+
COMCodePageToIconvCharset[WIN32OLE.codepage] or
|
25
|
+
raise 'no iconv charset associated with current COM codepage: %s' %
|
26
|
+
WIN32OLE.codepage
|
27
|
+
end
|
28
|
+
|
29
|
+
# Connects to a running COM object.
|
30
|
+
#
|
31
|
+
# This method shouldn’t be used directly, but rather is used by
|
32
|
+
# COM::Object.
|
33
|
+
#
|
34
|
+
# @param [String] id The program ID of the COM object to connect to
|
35
|
+
# @raise [COM::Error] Any error that may have occurred while trying to
|
36
|
+
# connect
|
37
|
+
# @return [COM::MethodMissing] The running COM object wrapped in a
|
38
|
+
# COM::MethodMissing
|
39
|
+
def connect(id)
|
40
|
+
Wrapper.new(WIN32OLE.connect(id))
|
41
|
+
rescue WIN32OLERuntimeError => e
|
42
|
+
raise Error.from(e)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Creates a new COM object.
|
46
|
+
#
|
47
|
+
# This method shouldn’t be used directly, but rather is used by
|
48
|
+
# COM::Object.
|
49
|
+
#
|
50
|
+
# @param [String] id The program ID of the COM object to create
|
51
|
+
# @param [String, nil] host The host of the program ID
|
52
|
+
# @raise [COM::Error] Any error that may have occurred while trying to
|
53
|
+
# create the COM object
|
54
|
+
# @return [COM::MethodMissing] The COM object wrapped in a COM::MethodMissing
|
55
|
+
def new(id, host = nil)
|
56
|
+
Wrapper.new(WIN32OLE.new(id, host))
|
57
|
+
rescue WIN32OLERuntimeError => e
|
58
|
+
raise Error.from(e)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# @private
|
65
|
+
COMCodePageToIconvCharset = {
|
66
|
+
WIN32OLE::CP_UTF8 => 'UTF-8'
|
67
|
+
}.freeze
|
68
|
+
end
|
69
|
+
|
70
|
+
require 'com/methodinvocationerror'
|
71
|
+
require 'com/pathname'
|
72
|
+
require 'com/standarderror'
|
73
|
+
require 'com/win32ole'
|
data/lib/com/error.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# Superclass for COM-related errors. This class is meant to be subclassed an
|
4
|
+
# used for refinement of otherwise very unspecific COM errors.
|
5
|
+
#
|
6
|
+
# @see COM::StandardError
|
7
|
+
class COM::Error < RuntimeError
|
8
|
+
class << self
|
9
|
+
# Creates a COM::Error from _error_, with optional _backtrace_. _Error_
|
10
|
+
# should be an error raised by WIN32OLE.
|
11
|
+
#
|
12
|
+
# This is an internal method.
|
13
|
+
#
|
14
|
+
# @param [WIN32OLERuntimeError] error The error to create a new one from
|
15
|
+
# @param [Array<String>, nil] backtrace The backtrace to use for the new
|
16
|
+
# error
|
17
|
+
def from(error, backtrace = nil)
|
18
|
+
errors.find{ |replacement| replacement.replaces? error }.replace(error).tap{ |e|
|
19
|
+
e.set_backtrace backtrace if backtrace
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
# @private method used by {.from}.
|
24
|
+
def replaces?(error)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def replace(error)
|
31
|
+
new(error.message)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def inherited(error)
|
37
|
+
errors.unshift error
|
38
|
+
end
|
39
|
+
|
40
|
+
def errors
|
41
|
+
@@errors ||= [self]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/com/events.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# Provides a simple wrapper around COM events. WIN32OLE provides no way of
|
4
|
+
# releasing an observer and thus causes an unbreakable chain of objects,
|
5
|
+
# causing uncollectable garbage. This class obviates most of the problems.
|
6
|
+
class COM::Events
|
7
|
+
ArgumentMissing = Object.new.freeze
|
8
|
+
|
9
|
+
# Creates a COM events wrapper for the COM object _com_’s _interface_.
|
10
|
+
# Optionally, any _events_ may be given as additional arguments.
|
11
|
+
#
|
12
|
+
# @param [WIN32OLE] com COM object implementing _interface_
|
13
|
+
# @param [String] interface Name of the COM interface that _com_ implements
|
14
|
+
# @param [Array<String>] events Names of events to register (see
|
15
|
+
# {#register})
|
16
|
+
def initialize(com, interface = nil)
|
17
|
+
@interface = WIN32OLE_EVENT.new(com, interface)
|
18
|
+
@events = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
# Observe _event_ in _block_ during the execution of _during_.
|
22
|
+
#
|
23
|
+
# @param [String] event Name of the event to observe
|
24
|
+
# @param [Proc] during Block during which to observe _event_
|
25
|
+
# @yield [*args] Event arguments (specific for each event)
|
26
|
+
# @return The result of _during_
|
27
|
+
def observe(event, observer = ArgumentMissing)
|
28
|
+
if ArgumentMissing.equal? observer
|
29
|
+
register event, Proc.new
|
30
|
+
return self
|
31
|
+
end
|
32
|
+
return self unless observer
|
33
|
+
observer = observer.to_proc
|
34
|
+
register event, observer
|
35
|
+
return self unless block_given?
|
36
|
+
begin
|
37
|
+
yield
|
38
|
+
ensure
|
39
|
+
unobserve event, observer
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def unobserve(event, observer = nil)
|
45
|
+
@events[event].delete observer if observer
|
46
|
+
if observer.nil? or @events[event].empty?
|
47
|
+
@interface.off_event event
|
48
|
+
@events.delete event
|
49
|
+
end
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def register(event, observer)
|
56
|
+
if @events.include? event
|
57
|
+
@events[event] << observer
|
58
|
+
return
|
59
|
+
end
|
60
|
+
@interface.on_event event do |*args|
|
61
|
+
@events[event].reduce({}){ |result, o|
|
62
|
+
r = o.call(*args)
|
63
|
+
result.merge! r if Hash === r
|
64
|
+
}
|
65
|
+
end
|
66
|
+
@events[event] = [observer]
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# Represents instantiable COM objects. These are COM objects that we can
|
4
|
+
# connect to and create.
|
5
|
+
class COM::Instantiable < COM::Object
|
6
|
+
class << self
|
7
|
+
# Gets or sets the COM program ID.
|
8
|
+
#
|
9
|
+
# If no program ID has explicitly been set, one based on the name of this
|
10
|
+
# class and its containing module. For example, A::B is turned into
|
11
|
+
# 'A.B'.
|
12
|
+
#
|
13
|
+
# @param [String,nil] id Program ID to, if given, use
|
14
|
+
# @return [String] The set or automatically generated program ID
|
15
|
+
# @raise [ArgumentError] If no program ID has been set and one can’t be
|
16
|
+
# automatically generated
|
17
|
+
def program_id(id = nil)
|
18
|
+
@id = id if id
|
19
|
+
return @id if defined? @id
|
20
|
+
raise ArgumentError,
|
21
|
+
'no automatic COM program ID for class available: %s' % self unless
|
22
|
+
matches = /^.*?([^:]+)::([^:]+)$/.match(name)
|
23
|
+
@id = '%s.%s' % matches[1..2]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Marks this class as trying to connect to already running instances of COM
|
27
|
+
# objects.
|
28
|
+
#
|
29
|
+
# @return true
|
30
|
+
def connect
|
31
|
+
@connect = true
|
32
|
+
end
|
33
|
+
|
34
|
+
# Queries whether or not this class tries to connect to already running
|
35
|
+
# instances of COM objects.
|
36
|
+
#
|
37
|
+
# The default is false.
|
38
|
+
#
|
39
|
+
# @return Whether or not this class tries to connect to already running
|
40
|
+
# instances of COM objects
|
41
|
+
#
|
42
|
+
# @see #connect
|
43
|
+
def connect?
|
44
|
+
@connect ||= false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sets whether or not this class tries to load constants when connecting to
|
48
|
+
# or creating a COM object.
|
49
|
+
#
|
50
|
+
# @param [Boolean] constants Whether or not to load constans
|
51
|
+
# @return [Boolean] Whether or not to load constants
|
52
|
+
def constants(constants)
|
53
|
+
@constants = constants
|
54
|
+
end
|
55
|
+
|
56
|
+
# Queries whether or not this class tries to load constans when connecting
|
57
|
+
# to or creating a COM object.
|
58
|
+
#
|
59
|
+
# The default is true.
|
60
|
+
#
|
61
|
+
# @return Whether or not to load constants
|
62
|
+
def constants?
|
63
|
+
return true unless defined? @constants
|
64
|
+
@constants
|
65
|
+
end
|
66
|
+
|
67
|
+
# Loads constants associated with COM object _com_. This is an internal
|
68
|
+
# method that shouldn’t be called outside of this class.
|
69
|
+
#
|
70
|
+
# @param [WIN32OLE] com COM object to load constants from
|
71
|
+
# @return [Boolean] Whether or not any constants where loaded
|
72
|
+
def load_constants(com)
|
73
|
+
return if constants_loaded?
|
74
|
+
modul = nesting[-2]
|
75
|
+
com.load_constants modul
|
76
|
+
@constants_loaded = true
|
77
|
+
end
|
78
|
+
|
79
|
+
# Queries whether constants have already been loaded for this class.
|
80
|
+
#
|
81
|
+
# @return Whether or not constants have already been loaded for this class
|
82
|
+
def constants_loaded?
|
83
|
+
@constants_loaded ||= false
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Gets the nesting of modules that lead up to this class.
|
89
|
+
#
|
90
|
+
# @return [Array<Module>] Modules that nest this class
|
91
|
+
def nesting
|
92
|
+
result = []
|
93
|
+
name.split(/::/).inject(Module) do |modul, name|
|
94
|
+
modul.const_get(name).tap{ |c| result << c }
|
95
|
+
end
|
96
|
+
result
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Connects to or creates a new instance of a COM object.
|
101
|
+
#
|
102
|
+
# @option options [Boolean] :connect (false) Whether or not to connect to a
|
103
|
+
# running instance (see {.connect})
|
104
|
+
# @option options [Boolean] :constants (true) Whether or not to load
|
105
|
+
# constants associated with the COM object (see {.constants})
|
106
|
+
def initialize(options = {})
|
107
|
+
@connected = false
|
108
|
+
connect if options.fetch(:connect, self.class.connect?)
|
109
|
+
self.com = COM.new(self.class.program_id) unless connected?
|
110
|
+
self.class.load_constants(com) if
|
111
|
+
options.fetch(:constants, self.class.constants?)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Queries whether or not an already running COM object was connected to.
|
115
|
+
# This is useful when deciding whether or not to close down the COM object
|
116
|
+
# when you’re done with it.
|
117
|
+
#
|
118
|
+
# @return Whether or not an already running COM object was connected to
|
119
|
+
def connected?
|
120
|
+
@connected
|
121
|
+
end
|
122
|
+
|
123
|
+
def inspect
|
124
|
+
'#<%s%s>' % [self.class, connected? ? ' connected' : '']
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Try to connect to a running COM object.
|
130
|
+
def connect
|
131
|
+
self.com = COM.connect(self.class.program_id)
|
132
|
+
@connected = true
|
133
|
+
rescue COM::OperationUnavailableError
|
134
|
+
end
|
135
|
+
end
|