C# COM object for VB6 that needs a Variant property - Property Set is failing

Russ Suter
Russ Suter used Ask the Experts™
I'm having to support an ancient VB6 application. Currently, I'm writing a COM object class for it. Everything works except for assigning a value to a property. This particular property needs to behave like a VB6 variant data type when used in VB6 code.

I have the simplest little class in C#
    [ComVisible(true)]
    [Guid("1EB35E9B-60EE-4A26-BAEB-6A2D7AC32FF0")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class FooThingy
    {
        private object _value;

        public object Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
            }
        }

        public FooThingy()
        {

        }
    }

Open in new window

And I call it via the simplest VB6 code. I have added line numbers for easy reference.
Public Sub Main()
    
1    Dim foo As FooThingy
2    Set foo = New FooThingy
3    If (foo.value <> "hello") Then
4        foo.value = "Testing"
5    End If

End Sub

Open in new window

At line 4 it fails with a runtime error 424 "Object required" yet line 3 evaluates with no problem. I know VB6 has this weird distinction between Property Let and Property Set and I'm guessing that is somehow related. What I need to figure out is how do I get my C# class to work with the VB6 code above. Perhaps there's some kind of marshaling declaration that will do the trick? I'm really hoping there's an answer to this because my alternative is to go through this very old VB6 program that was very badly written and find all the places where assignment is being done and replace it with a SetValue() method call which DOES work.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Try
Set foo.value = "Testing"
Russ SuterSenior Software Developer

Author

Commented:
That produces the same error but at compile time instead of runtime. You can't use the set statement to assign primitives.
Put a breakpoint on line 3 and check the value of foo.value using shift -f9
see if it has a valid value
Become a CompTIA Certified Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

Russ SuterSenior Software Developer

Author

Commented:
It does. The value is "Empty"
Try setting it's value to blank in the constructor. initial value should not be empty
Russ SuterSenior Software Developer

Author

Commented:
There's no reason why it cannot be empty. This is a Variant data type. Empty is absolutely a valid value and there is other VB6 code that performs checks for empty variant types. Blank is not an acceptable substitute for empty. "" does not equal NULL.

This isn't an object problem. It's a COM marshaling problem.
Russ SuterSenior Software Developer

Author

Commented:
Update
This works:
        [MarshalAs(UnmanagedType.Struct)]
        public object Value;

Open in new window

Unfortunately, this doesn't:
        [MarshalAs(UnmanagedType.Struct)]
        private object _value;
        [DispId(0)]
        public object Value
        {
            [return: MarshalAs(UnmanagedType.Struct)]
            get
            {
                return _value;
            }
            [param: MarshalAs(UnmanagedType.Struct)]
            set
            {
                _value = value;
            }
        }

Open in new window

and I need this to be a property because I have to perform certain sanity checks in the setter. Anyone have any idea how I can make this work with a property?
See if this article helps. It explains how to use managed code from unmanaged code

https://www.codeproject.com/Articles/154144/Using-Unmanaged-VB6-Code-in-NET
Russ SuterSenior Software Developer

Author

Commented:
Sorry. That doesn't address my issue of how to make the VB6 code in my original question function. It does give me an idea though. I'll need to check a few things and this could take some time. I'll get back to you.
Russ SuterSenior Software Developer

Author

Commented:
So this got me most of the way there. It turns out that if I create the interface as a VB6 class and compile it as an ActiveX DLL I can use that interface to create my C# class. The only downside is there's no constructor but in this case I don't need one since the class is created entirely in C# and never instantiated directly in VB6 code.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial