Как получить 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>
из этого например.