none
How to call a legacy COM collection by IEnumerable interface

    Question

  • I have one legacy COM,

    after I imported it into one C# project,

    it expose three function: _NewEnum, item, Count

    Now I can not walk through its items by foreach( ... in ... ) clause,

    it seems that _NewEnum function returns one "EnumeratorViewOfEnumVariant" object instead of IEnumerable instance object.

    How can I deal with this?

    the COM has only one dll file and no TLB/IDL file exist now.

    Wednesday, September 27, 2006 9:30 PM

All replies

  • That's odd, the IDE should automatically create a wrapper that converts the COM enumerator to GetEnumerator().  Read more about it here.  The tool library should be embedded in the resources of the DLL.
    Wednesday, September 27, 2006 10:41 PM
    Moderator
  • nobugz, Thanks for your feedback, it is a useful document and I've read it carefully.

    but it can not solve my problem here, VS IDE do not generate a wrapper named 'GetEnumerator()' for my COM lib,

    Here I attach one piece of tree view picture from ILDASM tool:

       |   |
       |   |___[INT] ClearQuestOleServer.IOAdQueryDefs
       |   |   |     .class interface public abstract auto ansi import
       |   |   |     .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( ...
       |   |   |     .custom instance void [mscorlib]System.Runtime.InteropServices.InterfaceTypeAttribute::.ctor(int16) = (...
       |   |   |     .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( // ...item.. ...
       |   |   |     .custom instance void [mscorlib]System.Runtime.InteropServices.TypeLibTypeAttribute::.ctor(int16) = ( ...
       |   |   |___[MET] Add : object(string)
       |   |   |___[MET] Load : object(string)
       |   |   |___[MET] Remove : bool(object)
       |   |   |___[MET] get_Count : int32()
       |   |   |___[MET] get__NewEnum : object()
       |   |   |___[MET] item : object(object&)
       |   |   |___[MET] set_Count : void(int32)
       |   |   |___[PTY] Count : int32()
       |   |   |___[PTY] _NewEnum : object()
       |   |
       |   |___[INT] ................(another interface)

     

    the cause is that this COM does not have some information for VS to generate IEnumerable interface,

    but, in old VB code, it is not a problem when you use ForEach clause.

    still the question: any way to let the c# navigate items by foreach clause?

    Thursday, September 28, 2006 3:14 AM
  • Well, tlbimp.exe clearly didn't recognize the COM enumerator in the component.  Guessing a bit, that might have something to do with the DISPIDs assigned to the methods.  COM enumerators are supposed to use specific values but, IIRC, VB6 didn't enforce that.  _NewEnum should have DISPID_NEWENUM (-4), Item should have DISPID_VALUE (0).  I found these in the Platform SDK's OAIdl.idl file.

    If you can still go back to the VB6 source code, assign the DISPIDs with Tools + Procedure Attributes, Advanced, Procedure ID combobox.
    Thursday, September 28, 2006 1:35 PM
    Moderator
  • There should be a FAQ of things you can't do with C#, C++ and COM.

    For example, you can't pass arrays into methods in C++ classes via COM.

    I wonder if this another one of those things you just can't do.
    Thursday, September 28, 2006 4:45 PM
  • I found it is the right DISPID you refered here. please see metadata shown in VS2005 below:

    [Guid("24A57450-3F6C-11D1-B2C0-00A0C9851B52")]
    [TypeLibType(2)]
    [ClassInterface(0)]
    public class OAdQueryDefsClass : IOAdQueryDefs, OAdQueryDefs
    {
    public OAdQueryDefsClass();
    [DispId(-4)]
    public virtual object _NewEnum { get; }

    [DispId(1)]
    public virtual int Count { get; set; }

    [DispId(2)]
    public virtual object Add(string PrimaryEntityDefName);

    [DispId(0)]
    public virtual object item(ref object Index);

    [DispId(3)]
    public virtual object Load(string filename);

    [DispId(4)]
    public virtual bool Remove(object Subscript);

    }

    I found a small message from MSDN help and it is maybe one possible solution for this,  it is to modify its IDL definiation, add a hint ( custom attribute ) for tlbimp.exe,


    ( code from MSDN, please refer to help on URL: ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_fxinterop/html/71734461-ddb0-4e69-a041-556596989363.htm)
    [
       object,
       uuid(40E86021-CAD7-493B-BF09-43811D821BA7),
       dual,
       helpstring("IMyClass Interface"),
       pointer_default(unique),
       // Use the IEnumerable custom attribute.
       custom(B64784EB-D8D4-4d9b-9ACD-0E30806426F7,"")
    ]
    interface IMyClass : IDispatch
    {
    };

    [
       uuid(3ACBCEB2-9D52-46FA-97E0-063310CFD776),
       helpstring("MyClass Class")
    ]
    coclass MyClass
    {
       [default] interface IMyClass;
    };

    but I can not got the idl for my COM library, it is a component from other company,

    if any way to put this hint in my C# code( like attribute attach to one class/interface), it maybe help to solve this issue, MSDN not describe how to implement it in C# code.

    Thursday, September 28, 2006 9:45 PM
  • For everyone, who search also for a solution:
    http://blogs.microsoft.co.il/pavely/2015/04/13/making-com-collections-easily-consumable-by-net/

    Tuesday, February 12, 2019 7:16 PM