2

I'm writing COM wrappers that return an object, or null if the object does not exist. When calling from VBScript, returning null throws the error "Object required: 'ComObj.Prop2'" Code 800A01A8...

C#

public class testCOM
{
    public object Func(int i)
    {
        if (i == 1) return new object();
        if (i == 2) return DBNull.Value;
        return null;
    }
}

VBScript

set ComObj = CreateObject("ClassLibrary1.testCOM")

set TestObj = ComObj.Func(1) 'This Works
set TestObj = ComObj.Func(2) 'Throws "Object required: 'ComObj.Func(...)'" Code 800A01A8
set TestObj = ComObj.Func(3) 'Throws "Object required: 'ComObj.Func(...)'" Code 800A01A8
set TestObj = Nothing        'This is what I want to occur with Func(2) and Func(3)

I tried returning DBNull.Value, which is supposed to marshal as VT_NULL, but no luck...

I really don't want to write a wrapper object similar to Nullabe<> with .HasValue and .Value... Another option I don't like is to create a Nothing object (inside no namespace) so I can do:

If TestObj Is Nothing And TestObj = "Nothing" Then
    'TestObj was nothing or "Nothing"
End If

If I use the Nothing object, then my COM object has to reutrn type Object instead of the expected typed object, making the code a bit harder to read though functionally the same for my purposes.

What is the best way to return a null object to VBScript via COM?

FuncL
  • 21
  • 4

2 Answers2

0

Check the signature in your interface and make sure it's correct. If I do the following:

public object test(int i) {
    object retval = null;
    if (i == 1) {
        retval = new Object();
    } else if (i == 2) {
        retval = DBNull.Value;
    }
    return retval;
}

with an interface signature of:

[DispId(1001)]
object test(int i);

and then, in VBScript, do:

' myObject is my instantiated COM class
WScript.Echo TypeName(myObject.test(1))
WScript.Echo TypeName(myObject.test(2))
WScript.Echo TypeName(myObject.test(3))

I get back:

Object
Null
Empty
Kijewski
  • 25,517
  • 12
  • 101
  • 143
basiphobe
  • 590
  • 6
  • 12
  • I appreciate the response. Unfortunately, your suggestion does not assign the returned object/value to a variable. Instead, it passes it directly to another function, TypeName. If your vbscript was: – FuncL Sep 17 '12 at 21:53
  • If your vbscript attempted `set TestObj = myObject.test(2)` in order to `Echo TypeName(TestObj)`, it would error with Code 800A01A8. Apparently, `set` requires an object to be set, so I cannot return null, which is not an object. If you leave off the 'set', it works. I have resorted to creating a Nothing class in the root namespace and testing `If TypeName(TestObj) = "Nothing" Then` – FuncL Sep 17 '12 at 22:32
  • Couldn't you just test with IsNull before attempting to Set it? – basiphobe Sep 19 '12 at 12:39
  • As you might guess from my keeping up with this thread, I've accepted the returning an instance of an global::Nothing object. But, to respond to using IsNull, I assume you mean `If Not IsNull(ComObj.Func(2)) Then Set TestObj = ComObj.Func(2)`. Unfortunately, that would execute the function twice, doubling the work. However, You did give me an idea. I can create `Function Wrapper(obj) If TypeName(obj) = "Empty" Then Set Wrapper = Nothing Else Set Wrapper = obj End If End Function`. Now, this should work: `Set TestObj = Wrapper(CommObj.Func(2)))`. Thanks for the help! – FuncL Oct 26 '12 at 02:00
0

Adding MarshalAs helps:

[return:MarshalAs(UnmanagedType.IDispatch)]
public object Func(int i)
{
    if (i == 1) return new object();
    if (i == 2) return DBNull.Value;
    return null;
}

I'm not sure why though. And if you have an interface then this attribute should be applied on interface level.

Konstantin Spirin
  • 20,609
  • 15
  • 72
  • 90