Avatar of Russ Suter
Russ Suter
 asked on

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

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.
Visual Basic.NET.NET ProgrammingC#* comVisual Basic Classic

Avatar of undefined
Last Comment
Russ Suter

8/22/2022 - Mon
Partha Mandayam

Try
Set foo.value = "Testing"
Russ Suter

ASKER
That produces the same error but at compile time instead of runtime. You can't use the set statement to assign primitives.
Partha Mandayam

Put a breakpoint on line 3 and check the value of foo.value using shift -f9
see if it has a valid value
Your help has saved me hundreds of hours of internet surfing.
fblack61
Russ Suter

ASKER
It does. The value is "Empty"
Partha Mandayam

Try setting it's value to blank in the constructor. initial value should not be empty
Russ Suter

ASKER
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.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Russ Suter

ASKER
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?
ASKER CERTIFIED SOLUTION
Partha Mandayam

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
Russ Suter

ASKER
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 Suter

ASKER
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.
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck