如何在C#中枚举COM对象的成员?

如何在C#中枚举COM对象的成员?,c#,.net,com,C#,.net,Com,我通过COM和接收系统连接到某个程序。我知道几种方法,所以我可以这样做: object result = obj.GetType().InvokeMember("SomeMethod", BindingFlags.InvokeMethod, null, obj, new object[] { "Some string" }); 像这样 dynamic dyn = obj; dyn.SomeMethod("Some string"); 两种方法都很好。但如何确定com对象的内部类型信息并枚举其

我通过COM和接收系统连接到某个程序。我知道几种方法,所以我可以这样做:

object result = obj.GetType().InvokeMember("SomeMethod", BindingFlags.InvokeMethod, null, obj, new object[] { "Some string" });
像这样

dynamic dyn = obj;
dyn.SomeMethod("Some string");
两种方法都很好。但如何确定com对象的内部类型信息并枚举其所有成员

我试过这个:

[ComImport, Guid("00020400-0000-0000-C000-000000000046"),
  InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDispatch
{
  void Reserved();
  [PreserveSig]
  int GetTypeInfo(uint nInfo, int lcid, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))] out System.Type typeInfo);
}

...

IDispatch disp = (IDispatch)obj;
Type t;
disp.GetTypeInfo(0, 0, out t);

但是t在末尾是空的。有人能帮我吗?

您无法获取COM对象的类型。这需要为COM组件创建一个互操作库。如果COM服务器有一个类型库,只需添加对它的引用或运行Tlbimp.exe实用程序,这无疑是一个低痛点。如果存在,则类型库通常嵌入在DLL中。当您获得这些信息时,编辑器和对象浏览器在COM类上可用的方法和属性方面变得更加智能

看到IDispatch cast工作,很可能还有一个类型库可用。对于COM服务器作者来说,创建一个是非常简单的。另一个可以用来查看类型库的工具是OleView.exe,View+Typelib

如果这不起作用,那么你确实可以从IDispatch中挖掘出一些东西。您的声明看起来可疑,IDispatch::GetTypeInfo的第三个参数是ITypeInfo,一个COM接口。不需要自定义封送拆收器,System.Runtime.InteropServices.ComTypes命名空间中提供了ITypeInfo。您可以使用Reflector从框架代码中挖掘IDispatch声明

当然,没有什么可以替代体面的文件。当您获得使用此组件的许可证时,您应该能够获得一些。您可以使用:


它有一个完整的API作为COM对象提供,可用于获取所有已注册COM对象的信息和函数并拦截它们。

我刚刚发表了一篇CodeProject文章,介绍了如何执行此操作。本文提供了一个小的C#
DispatchUtility
helper类,它很容易包含在其他项目中。在内部,它使用IDispatch的自定义声明和.NET的TypeToTypeInfoMarshaler将IDispatch的ITypeInfo转换为富.NET类型实例

在您的示例中,您可以调用
DispatchUtility.GetType(obj,true)
来获取.NET类型实例,然后可以在该实例上调用GetMembers

FWIW,
DispatchUtility
的IDispatch.GetTypeInfo声明与您的声明几乎相同。但是,在调用GetTypeInfo时,它会为lcid参数传递LOCALE\u SYSTEM\u DEFAULT(2048)而不是0。可能GetTypeInfo为您的
disp.GetTypeInfo(0,0,out t)
调用返回了一个失败的HRESULT。由于您使用
[PreserveSig]
声明了它,因此需要检查其结果(例如,通过调用
Marshal.throweexceptionforhr

以下是删除了大多数注释的
DispatchUtility
类的一个版本:

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.
    }
}
使用系统;
使用System.Runtime.InteropServices;
运用系统反思;
公共静态类DispatchUtility
{
private const int S_OK=0;//来自WinError.h
private const int LOCALE_SYSTEM_DEFAULT=2 0)
{
GetTypeInfo(0,区域设置\系统\默认值,输出结果);
}
if(result==null&&throwIfNotFound)
{
//如果调用的GetTypeInfoCount失败,则引发该异常。
元帅。通过hr(hr)的例外情况;
//否则,抛出Type.GetType将抛出的相同异常。
抛出新的TypeLoadException();
}
返回结果;
}
私有静态bool TryGetDispId(IDispatchInfo分派,字符串名称,out int dispId)
{
需求参考(调度,“调度”);
请求参考(名称,“名称”);
布尔结果=假;
Guid iidNull=Guid.Empty;
int hr=dispatch.GetDispId(ref iidNull,ref name,1,LOCALE\u SYSTEM\u默认值,out dispId);
const int DISP_E_UNKNOWNNAME=unchecked((int)0x80020006);//来自WinError.h
const int DISPID_UNKNOWN=-1;//来自OAIdl.idl
如果(hr==S_正常)
{
结果=真;
}
else if(hr==DISP\u E\u UNKNOWNNAME&&dispId==dispId\u UNKNOWN)
{
结果=假;
}
其他的
{
元帅。通过hr(hr)的例外情况;
}
返回结果;
}
[ComImport]
[接口类型(ComInterfaceType.InterfaceSiunknown)]
[Guid(“00020400-0000-0000-C000-0000000000 46”)]
专用接口IDispatchInfo
{
[信号]
int GetTypeInfoCount(out int typeInfoCount);
void GetTypeInfo(int-typeInfoIndex,int-lcid,[Marshallas(UnmanagedType.CustomMarshaler,
MarshalTypeRef=typeof(System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler))]输出类型typeInfo);
[信号]
int GetDispId(ref-Guid-riid、ref-string-name、int-namecont、int-lcid、out-int-dispId);
//注意:真正的IDispatch还有一个Invoke方法,但我们不需要它。
}
}

谢谢。事实上,该组件是一个内部带有脚本语言的业务应用程序。其成员的完整列表在运行时确定。而且它没有类型库。@HansPassant我在这里找到了对可疑的第三个参数的解释:这个CustomMarshaler太棒了!我一直在构建托管的TypeInfo和TypeLibraryInfo类来封装ITypeInfo和ITypeLib。这用一个我称之为GetCOMType的函数替换了那些大类。您可以从COM对象执行所有需要的调用和获取值。感谢您向我们展示这项令人敬畏的技术。+1真的,我修改了一些原始代码,但效果很好。当我得到类型t时,我可以枚举该类型的所有成员,不管该类型是.Net类型还是_ComObject类型(即使TypeInfo仅存在于内存中)。这应该被标记为正确答案(而不是说,我们不能,当我们显然可以的时候)