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