Win32ole with COM method taking two out refs

Hi,

I’ve got a challenge with a COM API that has a method with the
signature:

OpenApplication arg0, arg1, arg2, arg3, arg4
where
arg0, arg1, arg2 are String IN paramters (VT_BSTR)
arg3, arg4 are Variant OUT parameters (VT_BYREF|VT_VARIANT)

Arg3 and Arg4 are both objects returned by OpenApplication - in
particular, arg3 is a HsxServer object for getting (mostly useless) info
about the server, and arg4 is a HsvSession object needed for any useful
work.

I know I need to retrieve the values of arg3 and arg4 after the call to
OpenApplication using WIN32OLE::ARGV - the problem I have is: What do I
pass as placeholders for arg3 and arg4 so that the call will succeed,
and win32ole will know the types on return?

I’ve tried passing nil, which leads to a Type mismatch error.
I’ve tried passing Object.new, which allows the method to succeed and
returns WIN32OLE objects in ARGV[3] and ARGV[4]. However, these objects
have no associated type library information, so I then receive
“undefined method” errors if I try to call any methods on them.

In VB, the example code defines variables for arg3 and arg4 that are of
types defined in the type library (HsxServer and HsvSession), i.e.

Dim session as HsvSession
Dim server as HsxServer

Is there any way to achieve the same thing in Ruby with win32ole?

Many thanks,

Adam

On 4/7/06, Adam G. [email protected] wrote:

I’ve tried passing nil, which leads to a Type mismatch error.

Is there any way to achieve the same thing in Ruby with win32ole?

Many thanks,

Adam

WIN32OLE::VARIANT springs into mind, but I have no idea ;). RDoc can be
trusted though sometimes.

Cheers
Robert

Posted via http://www.ruby-forum.com/.


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

Hello,

In message “win32ole with COM method taking two out refs”
on 06/04/07, Adam G. [email protected] writes:

I’ve tried passing nil, which leads to a Type mismatch error.
I’ve tried passing Object.new, which allows the method to succeed and
returns WIN32OLE objects in ARGV[3] and ARGV[4]. However, these objects
have no associated type library information, so I then receive
“undefined method” errors if I try to call any methods on them.

What is the result of the following?
p ARGV[3]
p ARGV[4]

If the result is nil, then Win32OLE can not get ARGV[3] and ARGV[4].

In VB, the example code defines variables for arg3 and arg4 that are of
types defined in the type library (HsxServer and HsvSession), i.e.

Dim session as HsvSession
Dim server as HsxServer

Is there any way to achieve the same thing in Ruby with win32ole?

Unfortunately, there is no way to create variable defined in the type
library.

Regards,
Masaki S.

Hi Masaki,

Many thanks for your prompt response!

In answer to your question:

What is the result of the following?
p ARGV[3]
p ARGV[4]

When I pass Object.new, the above outputs:
#WIN32OLE:0x2a04408
#WIN32OLE:0x2a043f0

If I try adding p ARGV[4].ole_methods, I get the following error:
… in ‘ole_methods’: failed to GetTypeInfo (RuntimeError)

I’ve also tried:
session = WIN32OLE.new(‘Hyperion.HsvSession’)

This actually seems to work, in that the method appears to succeed, and
I’m then left with an object that has the right type library info
attached. However, I’m then getting a RuntimeError when I try to call
any method on the resulting session WIN32OLE object.

Any ideas how I might proceed?

Regards,

Adam

Hello,

In message “Re: win32ole with COM method taking two out refs”
on 06/04/08, Adam G. [email protected] writes:

… in ‘ole_methods’: failed to GetTypeInfo (RuntimeError)
It seems to me that your COM server does not provide type library
information to access from WIN32OLE.

I’ve also tried:
session = WIN32OLE.new(‘Hyperion.HsvSession’)

This actually seems to work, in that the method appears to succeed, and
I’m then left with an object that has the right type library info
attached. However, I’m then getting a RuntimeError when I try to call
any method on the resulting session WIN32OLE object.
Any ideas how I might proceed?

I have no good idea, but
could you try that the following work or not?

session = WIN32OLE.new(‘Hyperion.HsvSession’)
obj = session._invoke(0, [], [])
p obj

Regards,
Masaki S.

Hello,

In message “Re: win32ole with COM method taking two out refs”
on 06/04/10, Adam G. [email protected] writes:

session = WIN32OLE.new(‘Hyperion.HsvSession’)
obj = session._invoke(0, [], [])
p obj

This fails with the following error message:
hfm.rb:5:in `_invoke’: _invoke (WIN32OLERuntimeError)
OLE error code:0 in

HRESULT error code:0x80020003
Member not found. from hfm.rb:5

Hmm, I hope the session OLE object would have DISPID_VALUE method, but
not.

I would have expected this to get at least as far the other code. Any
idea why the WIN32OLE._invoke call doesn’t like the same parameters that
work when I call WIN32OLE.OpenApplication?

Sorry, but I do not have any idea.
How can I get the COM server what you are using?
If I can get the COM server, then I could investigate it more.

By the way, by using VBScript (not VB) can you realize what you want?
Unfortunately, if you can not, then you may not write the code
using Win32OLE.

Regards,
Masaki S.

Hi Masaki,

Masaki S. wrote:

Hello,

In message “Re: win32ole with COM method taking two out refs”
on 06/04/08, Adam G. [email protected] writes:

… in ‘ole_methods’: failed to GetTypeInfo (RuntimeError)
It seems to me that your COM server does not provide type library
information to access from WIN32OLE.

Looking at the type library, I see you are correct - the method
OpenApplication defines the server and session parameters as ppIUnknown.
If in VB I define the server and session parameters types as Object, I
cannot call any methods on them either.

could you try that the following work or not?

session = WIN32OLE.new(‘Hyperion.HsvSession’)
obj = session._invoke(0, [], [])
p obj

This fails with the following error message:
hfm.rb:5:in `_invoke’: _invoke (WIN32OLERuntimeError)
OLE error code:0 in

HRESULT error code:0x80020003
Member not found. from hfm.rb:5

The furthest I seem to be able to get is as follows:
server = WIN32OLE.new(‘Hyperion.HsxClient’)
session = WIN32OLE.new(‘Hyperion.HsvSession’)
OpenApplication cluster, product, app, server, session
session = WIN32OLE::ARGV[4]

This works without error, but then I get “HRESULT error code:0x80020009
Exception occurred.” if I try to call any method on the session object.

As an alternative, I’ve also tried using olegen.rb to generate a Ruby
class from the type library. However, I don’t seem to be able to pass an
object of the right type for my server and session parameters. Here is
the generated OpenApplication method:

VOID OpenApplication

method OpenApplication

BSTR arg0 — bstrClusterName [IN]

BSTR arg1 — bstrProduct [IN]

BSTR arg2 — bstrApp [IN]

UNKNOWN arg3 — ppIUnkServer [OUT]

UNKNOWN arg4 — ppIUnkSession [OUT]

def OpenApplication(arg0, arg1, arg2, arg3, arg4)
ret = @dispatch._invoke(13, [arg0, arg1, arg2, arg3, arg4],
[VT_BSTR, VT_BSTR, VT_BSTR, VT_BYREF|VT_VARIANT, VT_BYREF|VT_VARIANT])
@lastargs = WIN32OLE::ARGV
ret
end

When I create an instance of this class, I can call other methods prior
to OpenApplication without problems, e.g.
hfm = Hyperion_HFMClient.new()
server = hfm.GetServerOnCluster(‘HFDEV’)
session = WIN32OLE.new(‘Hyperion.HsvSession’)
hfm.SetLogonInfoSSO(‘’, userid, ‘’, password)
hfm.OpenApplication( cluster, product, app, server, session)

But the call to OpenApplication returns a type mismatch error no matter
what I try to pass in for the server and session parameters:
./hsx_client.rb:164:in _invoke': _invoke (WIN32OLERuntimeError) OLE error code:0 in <Unknown> <No Description> HRESULT error code:0x80020005 Type mismatch. from ./hsx_client.rb:164:in OpenApplication’
from hfm2.rb:10

I would have expected this to get at least as far the other code. Any
idea why the WIN32OLE._invoke call doesn’t like the same parameters that
work when I call WIN32OLE.OpenApplication?

Sorry to be a bother, but this is really bugging me, and I’d love to be
able to use Ruby for this bit of work.

Many Thanks,

Adam

Regards,
Masaki S.

Hi Masaki,

Masaki S. wrote:

I would have expected this to get at least as far the other code. Any
idea why the WIN32OLE._invoke call doesn’t like the same parameters that
work when I call WIN32OLE.OpenApplication?

Sorry, but I do not have any idea.
How can I get the COM server what you are using?
If I can get the COM server, then I could investigate it more.

Unfortunately, the software is commercial software and requires a
license to install. I don’t suppose there is anything I can check for
you at my end?

By the way, by using VBScript (not VB) can you realize what you want?
Unfortunately, if you can not, then you may not write the code
using Win32OLE.

Good suggestion! I have just tried equivalent scripts in VBScript and
JScript with Windows Scripting Host, and I’m bombing out even earlier.
(And by the way, neither of these scripting languages seems to provide
an equivalent to the really useful ole_methods etc - this is a great
feature of WIN32OLE!).

I guess I’m coming to the conclusion that this work might not be
possible using a scripting tool, although I don’t really see why this
has to be so. Then again, if the API to this product wasn’t so bad, I
wouldn’t have hit these issues! I guess my real frustration should be
with the vendor that provides such an appalling API…

Masaki, I very much appreciate your taking the time to help me with this
problem.

Thanks,

Adam

Adam G. wrote:

I guess I’m coming to the conclusion that this work might not be
possible using a scripting tool, although I don’t really see why this
has to be so.

Sounds like have a COM object but not a scriptable COM object. Only COM
objects which implement the IDispatch interface can be called from
late-binding scripting languages like VBScript and JScript.

VB (not VBScript) is able to use early-binding COM objects as well as
late-binding ones, so that’s why you got a “false positive” by using VB
instead of VBScript.

Jeff

Post your VB code…

I thought I’d summarise my conclusions here, on the off chance they may
help someone else with a similar problem… Although unfortunately, I
offer no solutions! :wink:

Jeff C. wrote:

Sounds like have a COM object but not a scriptable COM object. Only COM
objects which implement the IDispatch interface can be called from
late-binding scripting languages like VBScript and JScript.

VB (not VBScript) is able to use early-binding COM objects as well as
late-binding ones, so that’s why you got a “false positive” by using VB
instead of VBScript.

Actually, that is not the problem here. The COM objects do implement
IDispatch:
irb(main):004:0> p hfm.ole_methods
[QueryInterface, AddRef, Release, GetTypeInfoCount, GetTypeInfo,
GetIDsOfNames, Invoke, …

The problem seems to be that a key method (OpenApplication) which is
required to obtain references to other COM objects does so by using OUT
parameters which are declared to be of type Unknown, rather than the
actual type. Here is the method signature again:

VOID OpenApplication

method OpenApplication

BSTR arg0 — bstrClusterName [IN]

BSTR arg1 — bstrProduct [IN]

BSTR arg2 — bstrApp [IN]

UNKNOWN arg3 — ppIUnkServer [OUT]

UNKNOWN arg4 — ppIUnkSession [OUT]

As Ruby (along with VBScript, JScript, etc) is a dynamically typed
language, there is no way to declare the desired type of the arg3 and
arg4 parameters passed into (and out of, via ARGV) the OpenApplication
method, as you can do in VB:
Dim session as HsvSession

Passing an ordinary Object (Object.new) for arg3 and arg4 allows the
method call to succeed, but the returned objects can’t be used to call
any COM methods because WIN32OLE does not have any type info for them.

I think the reason the types are declared as Unknown in this instance is
because the type of the returned object (HsxServer and HsvSession
respectively) are defined in different (and separate) type libraries.
Exactly why this should be the case, I’ve no idea.

There are two ways I can see that this problem might still be overcome,
but both are beyond me:

  1. Create a dummy object of the correct COM type, i.e.

    server = WIN32OLE.new(‘Hyperion.HsxServer’)
    session = WIN32OLE.new(‘Hyperion.HsvSession’)
    hfm.OpenApplication(arg0, arg1, arg2, server, session)
    server = ARGV[3]
    session = ARGV[4]

This seemed quite promising, in that the call appeared to succeed, and
the returned objects had the necessary type library info attached.
However, any attempt to use these objects subsequently led to a “HRESULT
error code:0x80020009 Exception occurred” error. I’m not sure what is
causing this error (something to do with reference counting maybe?), but
I was unable to get past it.

  1. Enhance WIN32OLE to somehow allow type information to be added to an
    object once it has been created. What I’m getting at here is that after
    a successful call to OpenApplication using Object.new, I know that
    ARGV[3] is actually of type HsxServer and ARGV[4] is of type HsvSession,
    but WIN32OLE does not. If I could somehow assign type information to
    these out parameters that currently lack it, I could then call the COM
    methods on these objects.

I’m not sure how much effort this second option would be, but I figure
this problem is probably too specialised to warrant the effort.
Therefore, I’ll probably end up working around this via VB (arghh!) :frowning:

Cheers,

Adam

On Jan 22, 2010, at 9:13 AM, Tallak Tveide wrote:

exists that does the same (OpenOPC)
I don’t have an answer for you but I do have a suggestion. You might
want to try the pure ruby win32ole that is available on github [1]. It
may fix the specific problem you are encountering though it is equally
likely it has other problems. Give it a shot and let us know if it
worked.

cr

[1] GitHub - chef-boneyard/pr-win32ole: A pure Ruby implementation of the win32ole library

Chuck R. wrote:

On Jan 22, 2010, at 9:13 AM, Tallak Tveide wrote:

exists that does the same (OpenOPC)
I don’t have an answer for you but I do have a suggestion. You might
want to try the pure ruby win32ole that is available on github [1]. It
may fix the specific problem you are encountering though it is equally
likely it has other problems. Give it a shot and let us know if it
worked.

cr

[1] GitHub - chef-boneyard/pr-win32ole: A pure Ruby implementation of the win32ole library

Wow that’s interesting! I guess this means that OPC is available on
linux machines as well? Trying this right away :wink:

On Jan 22, 2010, at 9:33 AM, Tallak Tveide wrote:

cr

[1] GitHub - chef-boneyard/pr-win32ole: A pure Ruby implementation of the win32ole library

Wow that’s interesting! I guess this means that OPC is available on
linux machines as well? Trying this right away :wink:

Doubtful. The pure ruby win32ole relies on a C extension as one of its
dependencies. I imagine that still requires a Windows box.

cr

Hi

I realize this is an old thread, but I am struggeling, I think, with the
same issues. It occurs on a COM server that if freely available, namely
Matrikon OPC Simulator
(Free OPC Test Tools - MatrikonOPC). I
have attached the olegen file and also a script that fails in the same
manner as described.

I’m failrly sure that this should be scriptable as a pure python library
exists that does the same (OpenOPC)

What I did:

ruby olegen.rb ‘OPC Automation 2.0’ > olegen_opc_automation.rb

My application:

items = Object.new # output
errors = Object.new # output
group.OPCItems.AddItems 1, [‘’, ‘Random.Int1’], [0, 1], items, errors

The olegen code for the method looks like this:

VOID AddItems

Adds OPCItem objects to the collection

I4 arg0 — NumItems [IN]

BSTR arg1 — ItemIDs [IN]

I4 arg2 — ClientHandles [IN]

I4 arg3 — ServerHandles [OUT]

I4 arg4 — Errors [OUT]

VARIANT arg5 — RequestedDataTypes [IN]

VARIANT arg6 — AccessPaths [IN]

def AddItems(arg0, arg1, arg2, arg3, arg4, arg5=nil, arg6=nil)
ret = _invoke(1610743820, [arg0, arg1, arg2, arg3, arg4, arg5,
arg6], [VT_I4, VT_BYREF|VT_ARRAY|VT_BSTR, VT_BYREF|VT_ARRAY|VT_I4,
VT_BYREF|VT_ARRAY|VT_I4, VT_BYREF|VT_ARRAY|VT_I4, VT_VARIANT,
VT_VARIANT])
@lastargs = WIN32OLE::ARGV
ret
end

OPC is widely used in the automation industry, and I would so much like
to use ruby as a tool. The possibilities are endless, but OPC is a key
enabler for this to happen. So - any help is REALLLY appreciated :wink:

Btw: I’m using

ruby 1.9.1p243 (2009-07-16 revision 24175) [i386-mingw32]

Hello,
On Sat, Jan 23, 2010 at 12:13:09AM +0900, Tallak Tveide wrote:

Adds OPCItem objects to the collection

VT_BYREF|VT_ARRAY|VT_I4, VT_BYREF|VT_ARRAY|VT_I4, VT_VARIANT,
VT_VARIANT])
@lastargs = WIN32OLE::ARGV
ret
end

How about using WIN32OLE_VARIANT?

items = WIN32OLE_VARIANT(null, WIN32OLE::VARIANT::VT_NULL)
errors = WIN32OLE_VARIANT(null, WIN32OLE::VARIANT::VT_NULL)
group.OPCItems.AddItems 1, [’’, ‘Random.Int1’], [0, 1], items, errors
p items.value
p errors.value

WIN32OLE_VARIANT is available in Ruby 1.9 or later.
And you can’t use WIN32OLE_VARIANT with olegen code.
Could you try WIN32OLE_VARIANT without olegen code?

Regards,
Masaki S.

Ooops,

On Sat, Jan 23, 2010 at 07:15:21AM +0900, Masaki S. wrote:

How about using WIN32OLE_VARIANT?

items = WIN32OLE_VARIANT(null, WIN32OLE::VARIANT::VT_NULL)
errors = WIN32OLE_VARIANT(null, WIN32OLE::VARIANT::VT_NULL)

items = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)
errors = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)

Regards,
Masaki S.

Masaki S. wrote:

Ooops,

On Sat, Jan 23, 2010 at 07:15:21AM +0900, Masaki S. wrote:

How about using WIN32OLE_VARIANT?

items = WIN32OLE_VARIANT(null, WIN32OLE::VARIANT::VT_NULL)
errors = WIN32OLE_VARIANT(null, WIN32OLE::VARIANT::VT_NULL)

items = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)
errors = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)

Regards,
Masaki S.

Sorry - no help. The code now looks like this (without olegen):

#!/usr/bin/env ruby
require ‘rubygems’
require ‘win32ole’
opc_automation = WIN32OLE.new ‘Opc.Automation’
p opc_automation.GetOPCServers # ok
opc_automation.connect ‘Matrikon.OPC.Simulation’, ‘test.rb’
groups = opc_automation.OPCGroups
group = groups.add ‘test_rb_group’
items = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)
errors = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)
group.OPCItems.AddItems 1, [’’, ‘Random.Int1’], [0, 1], items, errors

[“ArchestrA.FSGateway.1”, “Matrikon.OPC.SiemensPLC.1”,
“ArchestrA.DASSIDirect.1”, “Prosys.OPC.Simulation”,
“Matrikon.OPC.Simulation.1”]
test.rb:28:in method_missing': (in OLE methodAddItems’: )
(WIN32OLERuntimeError)
OLE error code:0 in

HRESULT error code:0x80020005
Typekonflikt.
from test.rb:28:in `’

Sorry for beeing too late to reply.

On Tue, Jan 26, 2010 at 08:33:18PM +0900, Tallak Tveide wrote:

items = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)
errors = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)

Regards,
Masaki S.

Sorry - no help. The code now looks like this (without olegen):

items = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)
errors = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_NULL)
group.OPCItems.AddItems 1, [’’, ‘Random.Int1’], [0, 1], items, errors

How about VT_EMPTY instead of VT_NULL?
items = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_EMPTY)
errors = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_EMPTY)
group.OPCItems.AddItems 1, [’’, ‘Random.Int1’], [0, 1], items, errors

Regards,
Masaki S.

How about VT_EMPTY instead of VT_NULL?

This yields the same results as the example above.

Thanks again for your help :wink:

Tallak