none
C# class indexer access from VB6 via COM interop RRS feed

  • Question

  • Hello,

    I'm operating in a scenario where legacy VB6 code must interoperate with C# code. So far no problems except one special case where a C# library must substitude a former ActiveX-DLL.

    The problem that arises is the indexer of a C# class that is being consumed from VB6. I can get values using the indexer without issues but when trying to set values from VB6 I'm running into error 424 "Object required".

    Here's the C# code:

    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]  
    [ComVisible(true)]    
    public interface ITestList 
    {    
         [DispId(0)] 
         Object this[short Index] 
         { 
                [DispId(0)]set;
                [DispId(0)]get;          
         }        
    }
    
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    public class TestList : ITestList
    {
        private List<Object> internalList = new List<Object>();
    
        public TestList()
        {
        }        
    
        public Object this[Object Index]
        {
            [return: MarshalAs(UnmanagedType.Struct)]
            get
            {
                // code for setter goes here
                return "test";                   
            }
            [param: MarshalAs(UnmanagedType.Struct)]
            set
            {
                // code for setter goes here but even without code the error occurs
            }
        }
    
    }

    Access from VB6 code:

    'This is working
    MsgBox obj.Item(1)
    'This throws 424 "object required"
    obj.Item(1) = "test me"

    I've already investigated a lot but none of the findings solved the final problem. Please note that I'm not allowed to change the way VB6 is accessing the property. Thus the workaround to implement custom methods cannot be used.

    I'm sure there's a way to implement the indexer in a way it's setter can be accessed from VB6 or at least I'd want to understand why this won't work.

    Regards


    • Edited by Simbalight Tuesday, January 21, 2014 1:42 PM
    Tuesday, January 21, 2014 1:40 PM

Answers

  • I compiled your interface definition with Visual C# 2010 SP1, converted it to a type library with tlbexp.exe, and converted that to IDL with OLE/COM Object Viewer (in Windows SDK). The IDL was:

    // Generated .IDL file (by the OLE/COM Object Viewer)
    // 
    // typelib filename: <could not determine filename>
    
    [
      uuid(FEEADA9B-3314-4BD5-8F9F-9F405B883FDC),
      version(1.0)
    ]
    library vb6interop
    {
        // TLib :     // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
        importlib("stdole2.tlb");
    
        // Forward declare all types defined in this typelib
        dispinterface ITestList;
    
        [
          uuid(66F24A0C-7143-3841-9488-AE4672BEBA66),
          version(1.0),
            custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "vb6interop.ITestList")    
    
        ]
        dispinterface ITestList {
            properties:
            methods:
                [id(00000000), propputref]
                void Item(
                                [in] short Index, 
                                [in] VARIANT rhs);
                [id(00000000), propget]
                VARIANT Item([in] short Index);
        };
    };
    

    The problem seems to be that the accessor method has a propputref attribute, which corresponds to a "Set obj.prop = value" statement in VB6. Because you want "Let obj.prop = value" or just "obj.prop = value" to work, you need a method with the propput attribute. A property can have all three accessors (propget, propput, and propputref), and some Microsoft ActiveX controls do that.

    I don't know any way to make regasm.exe, tlbexp.exe, or TypeLibConverter generate a propput accessor for a property whose type is System.Object. There is INVOKE_PROPERTYPUT but I don't see any attribute class where you could specify that. I think you have the following options, but I did not test them:

    • Import the type library of the old ActiveX DLL to the C# library. Don't define the interface again; just implement it. This will ensure that the COM interface remains binary compatible. Then, I suppose you will have to change your installer to install and register the old COM type library too. There was a similar solution in the archived thread "COM Interop, Property Get,Let,Set Interface Attribute?".
    • Convert the problematic interface to IDL with OLE/COM Object Viewer, create a new IDL file with only that interface, and compile that to a type library with the MIDL compiler. Then import that to the C# library as above.
    • Export the type library from .NET to COM, but edit it to change the propputref to propput. I'm not sure how this can be automated, and the result might not be binary compatible with the old ActiveX DLL. This solution was used in the thread "COM Interop and propput/propputref".
    Saturday, January 25, 2014 11:46 AM

All replies

  • Hello,

    >>when trying to set values from VB6 I'm running into error 424 "Object required".

    As far as I know, this error is usually caused by when we want to change the value of one Collection item as:

    obj.Item(1) = "test me"

    Unfortunately, we can't edit values once they've been added to a collection. Collections don't work like arrays; we can't reassign an "element". So this is not possible:

    aColl.Item(aKey) = someValue, 

    We need delete the item and re-add the modified item as below:

    obj.Remove aKey
    
    obj.Add someValue, aKey
    

    Or we can create a class module which contains the properties

    For details, please refer to link below:

    http://social.msdn.microsoft.com/Forums/en-US/cd0ea68e-e76d-4aaf-948c-ac47e9b22d0f/how-do-i-change-the-value-of-a-collection-item?forum=isvvba

    There is a discussion regarding it.

    If I misunderstand, please let me know.

    Regards.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Wednesday, January 22, 2014 6:36 AM
    Moderator
  • Thank you for your hints Fred!

    But why do you think the compiler would treat the indexer as a collection?

    The original ActiveX-DLL I've got to replace allows this syntax:

    obj.Item(1) = "test me"

    The new C# (COM visible) DLL I'm implementing must allow the same.

    I've got one new finding that speaks against the collection suggestion: If I explicitly assign an object it would work:

    set obj.Item(1) = new MyObject()

    But the original DLL allows the same for strings, integers and the like.

    In Object Browser both Item-properties (old DLL and new DLL) are being reported as "Variant". Maybe the internal interpretation does not actually treat it as a variant?!

    I'm sure other people must have stumbled over that.

    Regards

    Wednesday, January 22, 2014 7:58 AM
  • Another addition: With an indexer of type String it will work, too. But I need an indexer that can cope with all types equally but then the assignment will fail as mentioned above.

    Any idea what's wrong or how it could be done?

    Regards

    Friday, January 24, 2014 2:47 PM
  • I compiled your interface definition with Visual C# 2010 SP1, converted it to a type library with tlbexp.exe, and converted that to IDL with OLE/COM Object Viewer (in Windows SDK). The IDL was:

    // Generated .IDL file (by the OLE/COM Object Viewer)
    // 
    // typelib filename: <could not determine filename>
    
    [
      uuid(FEEADA9B-3314-4BD5-8F9F-9F405B883FDC),
      version(1.0)
    ]
    library vb6interop
    {
        // TLib :     // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
        importlib("stdole2.tlb");
    
        // Forward declare all types defined in this typelib
        dispinterface ITestList;
    
        [
          uuid(66F24A0C-7143-3841-9488-AE4672BEBA66),
          version(1.0),
            custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "vb6interop.ITestList")    
    
        ]
        dispinterface ITestList {
            properties:
            methods:
                [id(00000000), propputref]
                void Item(
                                [in] short Index, 
                                [in] VARIANT rhs);
                [id(00000000), propget]
                VARIANT Item([in] short Index);
        };
    };
    

    The problem seems to be that the accessor method has a propputref attribute, which corresponds to a "Set obj.prop = value" statement in VB6. Because you want "Let obj.prop = value" or just "obj.prop = value" to work, you need a method with the propput attribute. A property can have all three accessors (propget, propput, and propputref), and some Microsoft ActiveX controls do that.

    I don't know any way to make regasm.exe, tlbexp.exe, or TypeLibConverter generate a propput accessor for a property whose type is System.Object. There is INVOKE_PROPERTYPUT but I don't see any attribute class where you could specify that. I think you have the following options, but I did not test them:

    • Import the type library of the old ActiveX DLL to the C# library. Don't define the interface again; just implement it. This will ensure that the COM interface remains binary compatible. Then, I suppose you will have to change your installer to install and register the old COM type library too. There was a similar solution in the archived thread "COM Interop, Property Get,Let,Set Interface Attribute?".
    • Convert the problematic interface to IDL with OLE/COM Object Viewer, create a new IDL file with only that interface, and compile that to a type library with the MIDL compiler. Then import that to the C# library as above.
    • Export the type library from .NET to COM, but edit it to change the propputref to propput. I'm not sure how this can be automated, and the result might not be binary compatible with the old ActiveX DLL. This solution was used in the thread "COM Interop and propput/propputref".
    Saturday, January 25, 2014 11:46 AM
  • Hi!

    Thank you a lot for your effort and explanations. This is most helpful information! It also shows me where there are gaps in my understanding of COM.

    I will pick one of the proposed approaches.

    Regards

    Wednesday, January 29, 2014 7:39 AM