Calling ActiveX control function with ref parameter from VB.NET

1

I have a VB.NET winforms app which uses an ActiveX control (ocx). Via interop, I have a reference to an object defined in the ocx and call various functions on it. This works ok for the most part.

I had some issues with one function call causing a memory leak (my other question was related to this) and the developer suggested I use a different function to get the same data. The difference is that the first function returns the data, and the second function is passed an object by ref in which it puts the data.

I call the original (leaky) function which returns the data like this:

// from generated documentation of axobject
// function name obfuscated
public virtual object A();

Called from VB.NET

Dim result = CType(axobject.A(), UInt16())

That works but memory leaks. So I will try to use the other function

This is the suggested function to use

// C++ definition, names obfuscated
long axobject::B(VARIANT* var)
// from generated documentation of axobject
// function name obfuscated
public virtual int B(out object var);

Calling it from VB.NET like this

Dim o As Object = Nothing
Dim i As Integer
i = axobject.B(o) ' exception here
Dim result = CType(o, UInt16())

produces an execption

System.Runtime.InteropServices.COMException: 'Parameter not optional. (Exception from HRESULT: 0x8002000F (DISP_E_PARAMNOTOPTIONAL))'

ok, so it seems like the null object is not passed to the function. So I assign something to it something beforehand

Dim o As Object = New Object()
Dim i As Integer
i = axobject.B(o)
Dim result = CType(o, UInt16())

and now this exception

System.Runtime.InteropServices.COMException: 'Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))'

Even tried boxing a UInt array

Dim o As Object = {1US}

but I get the same error.

Well my first inclination when I saw the definition was something like C# dynamic but it is not available in VB.NET and I've heard many times to use Object with Option Strict Off and Option Infer On. These are the options I am using, and it doesn't work. But I tested it in a C# project, and it works

dynamic o = null;
int res = axobject.B(ref o);
UInt16[] result = (UInt16[])o;
// everything here works

Here is a screenshot of the debugger with working code in C#.

enter image description here

The definition from the perspective of the C# project has ref instead of out when coming from VB.NET. I am 100% sure the ocx is the same version in both projects. This is strange

// from generated documentation of axobject in C# project
public virtual int B(ref object var);

Well I'm going to make an extension method in C# and see how it goes. Any suggestions on how to make this call purely from VB.NET would be very helpful.

Edit 1

I tried making an extension method in C# and calling from VB.NET. I get the same errors as VB.NET in the C# extension method

public static int B_ex(this axobject obj, ref object var)
{
    int res = obj.B(out dynamic o);
    var = o;
    return res;
}

DISP_E_PARAMNOTOPTIONAL

or when o is assigned to new object() first,

DISP_E_TYPEMISMATCH

Edit 2

Adding results of Ildasm

Interop.LibName.dll:

.method public hidebysig newslot virtual
instance int32 B(object& marshal( struct) var) runtime managed preservesig internalcall
{
.custom instance void [mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32) = ( 01 00 D1 00 00 00 00 00 )
.override LibName.ClassName::B
} // end of method ClassName::B

AxInterop.LibName.dll:

.method public hidebysig newslot virtual
instance int32 B(object& var) cil managed
{
// Code size 35 (0x23)
.maxstack 2
.locals init (int32 V_0)
IL_0000: ldarg.0
IL_0001: ldfld class [Interop.LibName]LibName.ClassName LibName.ClassName::ocx
IL_0006: brtrue.s IL_0014
IL_0008: ldstr "B"
IL_000d: ldc.i4.0
IL_000e: newobj instance void [System.Windows.Forms]System.Windows.Forms.AxHost/InvalidActiveXStateException::.ctor(string, valuetype [System.Windows.Forms]System.Windows.Forms.AxHost/ActiveXInvokeKind)
IL_0013: throw
IL_0014: ldarg.0
IL_0015: ldfld class [Interop.LibName]LibName.ClassName LibName.AxGetData::ocx
IL_001a: ldarg.1
IL_001b: callvirt instance int32 [Interop.LibName]LibName.ClassName::B(object&)
IL_0020: stloc.0
IL_0021: ldloc.0
IL_0022: ret
} // end of method ClassName::B

c#
vb.net
dynamic
com-interop
asked on Stack Overflow Apr 23, 2018 by djv • edited Apr 23, 2018 by djv

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0