views:

797

answers:

3

Given the following C# class:

namespace ComTest
{
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("A1D11BE5-40A1-4566-A1CA-418ABC76017C")]
    public interface IThing
    {
        [DispId(1)]
     void SetValue( object input );
        [DispId(2)]
     object Value {get; set;}
    }

    [ComVisible(true)]
    public class Thing: IThing
    {
     internal object PValue;

     public void SetValue(object input)
     {
      PValue = input;
     }

     public object Value
     {
      get
      {
       return PValue;
      }
      set
      {
       PValue = (object)value;
      }
     }

    }
}

Can anyone explain the following behaviour in VB6?

Dim oThing as Thing
Dim input as Variant, output as Variant
input = "Some text"
Set oThing = new Thing
oThing.SetValue(input)    ' works fine
output = oThing.Value     ' works fine
Set oThing.Value = input  ' fails with runtime error '424': Object required

[new] I set the assembly to be COM-visible and checked the project properties box marked "Register for COM interop" (Build tab).

As you may guess, the example is a cut down minimal version of the actual class I am using. I compared the generated TLB info for the actual class and the Thing class (below). The lines marked ** are found in the Thing class but not the actual class (the actual coclass contains [default] dispinterface IThing), and this extra generated interface _Thing appears to change the behaviour in VBA, in two ways: (1) the properties will no longer AutoComplete, (2) the direct assignment to Value now works as the contributors have found. Any thoughts?

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: ThingLib.tlb
[
  uuid(92093AA7-870E-498A-8B80-97545D221E24),
  version(1.0),
  helpstring("AAA Testing COM interop")
]
library ThingLib
{
    // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    importlib("mscorlib.tlb");
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    dispinterface IThing;
**  interface _Thing;

    [
      uuid(7FC255DD-F82F-4B39-8755-9680A97033B5),
      version(1.0),
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "ComTest.IThing")    

    ]
    dispinterface IThing {
        properties:
        methods:
            [id(0x00000001)]
            void SetValue([in] VARIANT input);
            [id(0x00000002), propget]
            VARIANT Value();
            [id(0x00000002), propputref]
            void Value([in] VARIANT rhs);
    };

    [
      uuid(D96FB9C7-A0AF-35D3-A0F6-A07A9ED47984),
      version(1.0),
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "ComTest.Thing")
    ]
    coclass Thing {
**      [default] interface _Thing;
        interface _Object;
        dispinterface IThing;
    };

**    [
**      odl,
**      uuid(C309BDD0-239F-38AA-A057-254F8E01BD4B),
**      hidden,
**      dual,
**      oleautomation,
**        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "ComTest.Thing")    
**    ]
**    interface _Thing : IDispatch {
**    };
};
+1  A: 

Jeremy, I tried your code and it works fine (although I used Excel VBA since I do not have VB6 installed). There is a typo in your code where

object Value (get; set;)

should be

object Value {get; set;}

but I doubt that it has anything to do with your error since it should give you a compile error. Did you try the code exactly as you put it here?

Jakob Christensen
A: 

I also tried your COM interface implementation with a VB6 client without any problems (with the exception of the typo Jakob mentioned in the Value property).

Jim H.
+4  A: 

Change your code to:

Dim oThing as Thing
Dim input as Variant, output as Variant
input = "Some text"
Set oThing = new Thing
oThing.SetValue(input)    ' works fine
output = oThing.Value     ' works fine
Set oThing.Value = input  ' Change this line

And everything should work. (You have to Set objects in VB6, you are trying to Let an object, which can only be done for atomic types).

Kris Erickson
You are right in that the code I gave works (when typo corrected) but the original class from which I copied it still does not work. See my edited question for COM (TLB) information differences between the original and the cut down example - I can't figure out what caused the difference.