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

Примечание. Для получения дополнительной информации см. Следующий связанный вопрос: Как получить ссылку LINQPad на систему Dump().__ComObject?

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

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

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

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

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
Система.__ComObject
ESRI.ArcGIS.Geometry.EnvelopeClass

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

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

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

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

Другие вопросы по тегам