Как получить System.Type класса Runtime Callable Wrapper из его CLSID?

Примечание. Дополнительные сведения см. в этом связанном вопросе: Как получить LINQPad для Dump() System.__ComObject references ?

Я могу получить CLSID класса RCW, соответствующего COM-объекту (полученному из другого COM-объекта, не инициализированного моим кодом), используя IPersist.GetClassID().

Type.GetTypeFromCLSID() всегда возвращает слабо- типизированный System.__ComObject, не строго типизированный класс RCW.

Мне нужно получить System.Type строго типизированного класса RCW, чтобы иметь возможность обернуть им COM-объект, используя Marshal.CreateWrapperOfType().

Возможно ли это или это невозможно из-за того, как работает COM-взаимодействие?


person blah238    schedule 07.02.2013    source источник
comment
Второй параметр, передаваемый CreateWrapperOfType, должен быть определен в пространстве .NET (с атрибутами ComImport и т. д.), поэтому он не будет работать без определения этого типа каким-либо образом (с помощью кода C#, tlbimp или Reflection Emit).   -  person Simon Mourier    schedule 07.02.2013
comment
У меня есть PIA с определенными RCW (дополнительную информацию см. в соответствующем вопросе).   -  person blah238    schedule 07.02.2013
comment
Да, это не меняет ответа :-) ваша задача — предоставить тип, поскольку в пространстве .NET может существовать бесконечное множество типов, определяющих соответствующий COM-объект. Итак, если у вас есть PIA, вы можете просмотреть все классы в пространстве имен PIA (например, используя Reflection), создать Dictionary<Guid, Type> и предоставить тип из этого словаря, когда у вас есть guid.   -  person Simon Mourier    schedule 07.02.2013
comment
Я так понял, это то, над чем я начал работать, прежде чем подумал, что должен быть лучший способ. Функция GetTypeFromCLSID возродила мои надежды, но затем развеяла их, когда я прочитал, что она всегда возвращает System.__ComObject :p Это может оказаться неработоспособным, учитывая, насколько велики библиотеки объектов, с которыми я работаю.   -  person blah238    schedule 07.02.2013
comment
Этот вопрос выглядит актуальным: Получить тип из GUID   -  person blah238    schedule 07.02.2013


Ответы (2)


Что ж, вот то, что я собрал в качестве доказательства концепции, на самом деле всего несколько методов расширения. Это зависит от COM-объекта, реализующего IPersist и имеющего класс RCW в одном из PIA, загруженных в текущий AppDomain.

internal static class ExtensionMethods
{
    internal static object ConvertToRCW(this object o)
    {
        var guid = o.GetCLSID();
        if (guid != Guid.Empty)
        {
            return Marshal.CreateWrapperOfType(o, o.GetTypeFromGuid(guid));
        }
        else
        {
            return o;
        }
    }

    internal static Guid GetCLSID(this object o)
    {
        Guid guid = Guid.Empty;
        var p = o as IPersist;
        if (p != null)
            p.GetClassID(out guid);
        return guid;
    }

    internal static Type GetTypeFromGuid(this object o, Guid guid)
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        foreach (var assembly in assemblies)
        {
            var types = assembly.GetLoadableTypes();
            foreach (var type in types)
            {
                if (type.GUID == guid)
                    return type;
            }
        }
        return o.GetType();
    }

    internal static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
    {
        try
        {
            return assembly.GetTypes();
        }
        catch (ReflectionTypeLoadException e)
        {
            return e.Types.Where(t => t != null);
        }
    }
}

Используется следующим образом:

var point = new ESRI.ArcGIS.Geometry.Point();
point.PutCoords(1, 1);
Console.WriteLine(point.GetType().FullName);
Console.WriteLine(point.Envelope.GetType().FullName);
Console.WriteLine(point.Envelope.ConvertToRCW().GetType().FullName);

Я получаю следующий вывод:

ESRI.ArcGIS.Geometry.PointClass
System.__ComObject
ESRI.ArcGIS.Geometry.EnvelopeClass

Что и было желаемым результатом. Теперь, чтобы сделать эту игру приятной с LINQPad (мой исходный вопрос).

person blah238    schedule 08.02.2013
comment
откуда я могу получить определение IPersist? - person David Thielen; 25.10.2018

Type.GetTypeFromCLSID() просто возвращает динамическую оболочку COM.

RCW со строгим типом должен быть определен в пространстве .NET. Это должны быть классы с атрибутом ComImportAttribute. .NET не может автоматически создавать эти классы ex-hihilo. Они определяются вручную (в коде .NET), или с помощью PIA, или с помощью tlbimp, или, например, с помощью механизма Reflection Emit, как и все типы .NET. Между CLSID COM и соответствующими классами .NET нет предустановленной связи, поскольку может существовать несколько классов .NET, соответствующих одному и тому же CLSID.

Если у вас есть эти типы, вы можете просканировать определенный набор пространств имен и, например, построить из него Dictionary<Guid, Type>.

person Simon Mourier    schedule 07.02.2013
comment
Я просто хотел сказать, что это это полезно, и спасибо. Я думаю, что в моем случае CLSID гарантированно однозначно привязан к конкретному строго типизированному RCW, но я могу получить CLSID в первую очередь только в том случае, если COM-объект реализует IPersist (полезный процент библиотеки, с которой я работаю с делает, иначе я бы не заморачивался). Однако было бы неплохо, если бы для этого существовал предопределенный механизм. - person blah238; 08.02.2013
comment
Вы также можете проверить, реализует ли COM-объект IProvideClassInfo: msdn.microsoft.com/fr-fr/library/windows/desktop/. Оттуда вы можете получить ITypeInfo, а оттуда тип GUID: stackoverflow.com/questions/4651869/, но вам нужно будет определить все эти интерфейсы в своем коде: -) - person Simon Mourier; 08.02.2013
comment
Да, к сожалению, ни один из них не реализует IProvideClassInfo, и лишь немногие реализуют IDispatch. - person blah238; 08.02.2013