How to completely terminate a WIN32OLE Excel object?


#1

Hello,

I can’t get rid of Excel, if I have ever called it,
even with such a simple script:

---------- code starts --------------
require ‘win32ole’

def puts_win32ole_objects
res = []
ObjectSpace.each_object do |o|
res << o if o.is_a? WIN32OLE
end
puts res.inspect
end

puts_win32ole_objects # --> empty
xl = WIN32OLE.new(‘Excel.Application’)
puts_win32ole_objects # --> one object
xl.Quit
xl = nil
GC.start # anything else I could do???
puts_win32ole_objects # --> the object is still there
sleep 5

And also the Excel process remains running in Windows.

Btw, even after the xl.Quit, the object remains fully functional,
so I could call o.Visible, o.Workbooks.Open(…) etc., it o were
the object (retrieved from ObjectSpace).

When the Ruby process is terminated (after sleep), the
Excel process will also terminate.

So, what’s the proper way to handle this?

I’ve found the WIN32OLE.ole_free method, but they say
that it is for debugging only.

Thanks for your help

Sven


#2

Sven S. schrieb:

I can’t get rid of Excel, if I have ever called it,

And I forgot:
This happens both with Ruby 1.8.6 and 1.8.5.

Sven


#3

I can’t replicate this:

require ‘win32ole’
=> true

?> def puts_win32ole_objects

res = []
ObjectSpace.each_object do |o|
?> res << o if o.is_a? WIN32OLE

 end

puts res.inspect
end
=> nil

?> puts_win32ole_objects # --> empty
[]
=> nil

xl = WIN32OLE.new(‘Excel.Application’)
=> #WIN32OLE:0x34e20fc

xl.Visible
=> false

puts_win32ole_objects # --> one object
[#WIN32OLE:0x34e20fc]
=> nil

puts tasklist | find /I "excel.exe"
EXCEL.EXE 508 Console 0 10,132 K
=> nil

xl.Quit
=> nil

xl = nil
=> nil

GC.start # anything else I could do???
=> nil

puts_win32ole_objects # --> the object is still there
[]
=> nil

puts tasklist | find /I "excel.exe"
=> nil


#4

Hello James,

James T. schrieb:

I can’t replicate this:

require ‘win32ole’

[… skipping irb session …]

This is truely strange!

Thanks for pointing this out!

When I tried this with IRB on my machine,
I got the same result:
Excel termination worked normally.

So there must be some difference
when the is executed as a Ruby file vs. via IRB.

So far, I have no idea what it could be.
I have set both RUBYOPT and RUBLIB to empty strings,
I’ve run the file via SciTe and on the console (ruby filename.rb)
– the Excel object persists under all those conditions.
It’s only within IRB that it is cleaned up properly.

Both my Ruby186 and 185 installations were done with
the one-click installer. (if that is of any significance)

I would appreciate any further help on this issue.

Thank you
Sven


#5

Sven S. wrote:

This is truely strange!

Thanks for pointing this out!

When I tried this with IRB on my machine,
I got the same result:
Excel termination worked normally.

So there must be some difference
when the is executed as a Ruby file vs. via IRB.

Interesting indeed!

I can replicate from a .rb file.

In fact, loading the .rb in irb also works in the correct manner, on the
contrary, if I load the .rb in my hacked version of IRB, it does not.
This seems almost like it is hardcoded to work for irb, or it is
something to do with the way in which handles are being processed.

I have also noticed a similar behavior with shell execution.

A GC.start will clear used file handles from `` and system() calls in
IRB, and if documentation I have read elsewhere is correct, this is not
the case for ‘normal execution’. Time to plough through IRBs source
tree, and start tracing.


#6

Hello,

In message “How to completely terminate a WIN32OLE Excel object?”
on 07/05/26, Sven S. removed_email_address@domain.invalid writes:

When the Ruby process is terminated (after sleep), the
Excel process will also terminate.

So, what’s the proper way to handle this?

I’ve found the WIN32OLE.ole_free method, but they say
that it is for debugging only.

I am not sure, but it seems to me because of GC behavior.
I tested your script using Foo class instead of WIN32OLE.
And I’ve got same result as WIN32OLE.

— code starts —
class Foo
def initialize(arg)
@arg = arg
end
def Quit
end
end

def puts_win32ole_objects
res = []
ObjectSpace.each_object do |o|
res << o if o.is_a? Foo
end
puts res.inspect
end

puts_win32ole_objects # --> empty
xl = Foo.new(‘Excel.Application’)
puts_win32ole_objects # --> one object
xl.Quit
xl = nil
GC.start # anything else I could do???
puts_win32ole_objects # --> the object is still there
sleep 5
puts_win32ole_objects # --> the object is still there
— code end —

The Excel process is terminated when the WIN32OLE object is GCed.
But in this case, the WIN32OLE object is not GCed, so the Excel
process is not terminated.

I recommend that you do not use WIN32OLE#ole_free(See following
REMARK!).
But if you want to terminate the process before the WIN32OLE object
GCed,
you can use WIN32OLE#ole_free.

REMARK!
WIN32OLE#ole_free terminates the Excel process but
the WIN32OLE object is not GCed.
You should not access the WIN32OLE object after WIN32OLE#ole_free
called.

Regards
Masaki S.


#7

Masaki S. schrieb:

I am not sure, but it seems to me because of GC behavior.
I tested your script using Foo class instead of WIN32OLE.
And I’ve got same result as WIN32OLE.

I cannot confirm that.
The last two outputs give the empty list on my system.
Independent of the Ruby version (1.8.6 or 1.8.5).

res = []
xl = nil
GC.start # anything else I could do???
puts_win32ole_objects # --> the object is still there
sleep 5
puts_win32ole_objects # --> the object is still there
— code end —

The Excel process is terminated when the WIN32OLE object is GCed.
But in this case, the WIN32OLE object is not GCed, so the Excel
process is not terminated.

I see: it is all a matter of garbage collection.

But if you want to terminate the process before the WIN32OLE object GCed,
you can use WIN32OLE#ole_free.

Thanks for telling me – this worked indeed, contrary to
WIN32OLE.ole_free.
It would be a dirty solution.

However, The point you made about GC, has inspired me towards further
experiments.
First, I created a second WIN32OLE.new(‘Excel Application’), just to see
if the first object might get ‘recycled’.
What I found was, that two objects existed after the second object
creation,
but after the three terminating statements (quit – nil – GC),
only the second object remained. (Lazy recycling, … :slight_smile: )

Encouraged by this surprising result, I tried other things, and then
this:
xl = Foo.new(‘Excel.Application’)
puts_win32ole_objects
xl = nil
GC.start

Just not calling xl.Quit anymore, and – the object is gone!
Simplest of all!

This looks like an easy solution, but no no, too early to jump for joy:
I encountered this behaviour for any method I called on the xl object.
OK, so, the easy destruction only works for “virgin” objects.

Thus, so far, we have not yet found a gentle
way to finish a WIN32OLE/Excel object.
For the time being, I think I’ll resort to
(not-so-gentle) WIN32OLE#ole_free.

Regards,
Sven