Acabo de publicar un artículo de CodeProject sobre cómo hacer Reflection with IDispatch-based COM objects. El artículo proporciona una pequeña clase de ayuda C# DispatchUtility
que es fácil de incluir en otros proyectos. Internamente, utiliza una declaración personalizada de IDispatch y TypeToTypeInfoMarshaler de .NET para convertir ITypeInfo de IDispatch en una instancia rica de tipo .NET.
En su ejemplo, puede llamar al DispatchUtility.GetType(obj, true)
para recuperar una instancia de tipo .NET, que luego puede llamar a GetMembers.
FWIW, DispatchUtility
La declaración de IDispatch.GetTypeInfo es casi idéntica a la suya. Sin embargo, al llamar a GetTypeInfo, pasa en LOCALE_SYSTEM_DEFAULT (2048) en lugar de 0 para el parámetro lcid. Quizás GetTypeInfo devolvió un error HRESULT para su llamada disp.GetTypeInfo(0, 0, out t)
. Como lo declaró con [PreserveSig]
, deberá verificar su resultado (por ejemplo, llamando al Marshal.ThrowExceptionForHR
).
Aquí hay una versión de la clase DispatchUtility
con la mayoría de los comentarios eliminados:
using System;
using System.Runtime.InteropServices;
using System.Reflection;
public static class DispatchUtility
{
private const int S_OK = 0; //From WinError.h
private const int LOCALE_SYSTEM_DEFAULT = 2 << 10; //From WinNT.h == 2048 == 0x800
public static bool ImplementsIDispatch(object obj)
{
bool result = obj is IDispatchInfo;
return result;
}
public static Type GetType(object obj, bool throwIfNotFound)
{
RequireReference(obj, "obj");
Type result = GetType((IDispatchInfo)obj, throwIfNotFound);
return result;
}
public static bool TryGetDispId(object obj, string name, out int dispId)
{
RequireReference(obj, "obj");
bool result = TryGetDispId((IDispatchInfo)obj, name, out dispId);
return result;
}
public static object Invoke(object obj, int dispId, object[] args)
{
string memberName = "[DispId=" + dispId + "]";
object result = Invoke(obj, memberName, args);
return result;
}
public static object Invoke(object obj, string memberName, object[] args)
{
RequireReference(obj, "obj");
Type type = obj.GetType();
object result = type.InvokeMember(memberName,
BindingFlags.InvokeMethod | BindingFlags.GetProperty,
null, obj, args, null);
return result;
}
private static void RequireReference<T>(T value, string name) where T : class
{
if (value == null)
{
throw new ArgumentNullException(name);
}
}
private static Type GetType(IDispatchInfo dispatch, bool throwIfNotFound)
{
RequireReference(dispatch, "dispatch");
Type result = null;
int typeInfoCount;
int hr = dispatch.GetTypeInfoCount(out typeInfoCount);
if (hr == S_OK && typeInfoCount > 0)
{
dispatch.GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, out result);
}
if (result == null && throwIfNotFound)
{
// If the GetTypeInfoCount called failed, throw an exception for that.
Marshal.ThrowExceptionForHR(hr);
// Otherwise, throw the same exception that Type.GetType would throw.
throw new TypeLoadException();
}
return result;
}
private static bool TryGetDispId(IDispatchInfo dispatch, string name, out int dispId)
{
RequireReference(dispatch, "dispatch");
RequireReference(name, "name");
bool result = false;
Guid iidNull = Guid.Empty;
int hr = dispatch.GetDispId(ref iidNull, ref name, 1, LOCALE_SYSTEM_DEFAULT, out dispId);
const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); //From WinError.h
const int DISPID_UNKNOWN = -1; //From OAIdl.idl
if (hr == S_OK)
{
result = true;
}
else if (hr == DISP_E_UNKNOWNNAME && dispId == DISPID_UNKNOWN)
{
result = false;
}
else
{
Marshal.ThrowExceptionForHR(hr);
}
return result;
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00020400-0000-0000-C000-000000000046")]
private interface IDispatchInfo
{
[PreserveSig]
int GetTypeInfoCount(out int typeInfoCount);
void GetTypeInfo(int typeInfoIndex, int lcid, [MarshalAs(UnmanagedType.CustomMarshaler,
MarshalTypeRef = typeof(System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler))] out Type typeInfo);
[PreserveSig]
int GetDispId(ref Guid riid, ref string name, int nameCount, int lcid, out int dispId);
// NOTE: The real IDispatch also has an Invoke method next, but we don't need it.
}
}
Gracias. De hecho, este componente es una aplicación de negocios con un lenguaje de secuencias de comandos dentro. La lista completa de sus miembros se determina en el tiempo de ejecución. Y no tiene una biblioteca de tipos. –
@HansPassant Me encontré con una explicación para el tercer parámetro sospechoso aquí: https://www.codeproject.com/articles/523417/reflection-with-idispatch-based-com-objects – jnm2