C#COM服务器从GetIDsOfNames返回E#U指针

C#COM服务器从GetIDsOfNames返回E#U指针,c#,comobject,idispatch,ole-automation,C#,Comobject,Idispatch,Ole Automation,我试图在C#中创建一个COM服务器,以公开一个OLE自动化对象。我想手动实现IDispatch接口,因为我需要对成员进行一些动态查找。当我调用我的自定义GetIDsOfNames时,C方法运行,然后客户端使用E_指针出错。但是让我们从头开始 经过几个小时的阅读和大量的尝试和错误,我已经设法拼凑出一些我认为应该有用的东西。我有一个非常简单的界面 [Guid("7B95C174-9320-4400-88AB-6960B019CEDE")] [ComVisible(true)] public inte

我试图在C#中创建一个COM服务器,以公开一个OLE自动化对象。我想手动实现IDispatch接口,因为我需要对成员进行一些动态查找。当我调用我的自定义
GetIDsOfNames
时,C方法运行,然后客户端使用
E_指针出错。但是让我们从头开始

经过几个小时的阅读和大量的尝试和错误,我已经设法拼凑出一些我认为应该有用的东西。我有一个非常简单的界面

[Guid("7B95C174-9320-4400-88AB-6960B019CEDE")]
[ComVisible(true)]
public interface ISimpleObject {
    EchoObject testNest();
}
它可以返回另一个COM对象。第一个COM对象使用默认的C#IDispatch实现,工作正常。EchoObject是一个带有自定义IDispatch实现的对象,因为它不是C#SDK的一部分。让我们看看我是如何定义它的

[ComImport]
[Guid("00020400-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDispatch {
    int GetTypeInfoCount(out uint pctinfo);
    int GetTypeInfo(uint iTInfo, int lcid, out IntPtr info);

    int GetIDsOfNames(
        ref Guid iid,
        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] names,
        int cNames,
        int lcid,
        [Out][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] int[] rgDispId);

    int Invoke(
        int dispId,
        ref Guid riid,
        int lcid,
        System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags,
        ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
        [Out][MarshalAs(UnmanagedType.LPArray)] object[] result,
        IntPtr pExcepInfo,
        IntPtr puArgErr);
}
我在IEchoObject接口的定义中使用定义的IDispatch接口

[Guid("D3E9B530-DFD5-4B54-88B9-4FBC8B0926E2")]
[ComVisible(true)]
public interface IEchoObject: IDispatch {
    string testint();
}
然后,该接口的实现必须实现ICustomQueryInterface,以允许IDispatch转换进行

[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IDispatch))]
[Guid("F625A833-3B4E-455D-90A9-89B1953147CD"), ComVisible(true)]
public class EchoObject : ReferenceCountedObject, IEchoObject, ICustomQueryInterface {
    public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv) {
        Console.WriteLine("Are we a: " + iid + "?");
        ppv = IntPtr.Zero;
        if (typeof(IDispatch).GUID == iid)
        {
            Console.WriteLine("Yes");
            ppv = Marshal.GetComInterfaceForObject(this, typeof(IDispatch), CustomQueryInterfaceMode.Ignore);
            return CustomQueryInterfaceResult.Handled;
        }
        return CustomQueryInterfaceResult.NotHandled;
    }

    public int GetIDsOfNames(
        ref Guid iid,
        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] names,
        int cNames,
        int lcid,
        [Out][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] int[] rgDispId
    ) {
        bool unknown = false;

        if(cNames == 1) {
            Console.WriteLine("Getting id of " + names[0]);
            if (names[0] == "testman") {
                Console.WriteLine("Member found");
                rgDispId[0] = 1;
            } else {
                rgDispId[0] = -1;
            }
        }

        return unknown ? HRESULT.DISP_E_UNKNOWNNAME : HRESULT.S_OK;
    }
}
我当然已经实现了IDispatch的其他方法,但我不认为它们对我遇到的问题很重要。请注意,GetIDsOfNames打印到控制台

我可以很好地创建初始的
SimpleObject
。我还可以对其调用
testNest
方法,并获取
EchoObject
实例。但是,当在该
EchoObject
上查找
testman
方法的ID时,客户端从win32返回一个
E_指针(无效指针),而不是结果

我已经用Excel、VBScript和一个定制的C++ COM客户机测试了它。都得到一个无效指针错误,C++客户端显示它来自代码> GETDISSONDISS/<代码>。奇怪的是,C语言方法运行时,它愉快地将该行打印到控制台,然后返回,然后C++ COM客户端会得到一个错误。 如果禁用所有自定义IDispatch,而使用默认的C#实现(调用testint方法而不是testman),则效果很好。我怀疑我的编组可能在某个地方出错,但我已经盯着它看了好几个小时,看不出问题所在


编辑:我做了另一个测试。如果我将GetIDsOfNames的返回类型从int更改为void,它将起作用。那里发生了什么事?根据示例,返回类型是HRESULT(一个32位带符号的int),但是当我返回时,我会得到一个错误。

@HansPassant通常你可能是对的,但是对于我的用例,我不知道编译时有哪些方法可用,所以我需要在运行时查找它们。这在自动IDispatch实现中是不可能的。IReflect接口也不适用,因为我仍然需要知道启动时可以提供的所有方法。@HansPassant我不明白我自己的要求怎么可能是错误的。我正在COM和一些外部系统之间实现一个代理。我不知道这个外部系统的对象有哪些方法,但是如果我有一个名字,我可以出去问它是否有那个特定的成员。我不知道有什么方法可以在不亲自实现IDispatch的情况下实现这一点。默认的IDispatch实现只回答编译时已知的成员(它们必须在接口中定义)。@HansPassant通常您可能是对的,但对于我的用例,我不知道编译时有哪些方法可用,所以我需要在运行时查找它们。这在自动IDispatch实现中是不可能的。IReflect接口也不适用,因为我仍然需要知道启动时可以提供的所有方法。@HansPassant我不明白我自己的要求怎么可能是错误的。我正在COM和一些外部系统之间实现一个代理。我不知道这个外部系统的对象有哪些方法,但是如果我有一个名字,我可以出去问它是否有那个特定的成员。我不知道有什么方法可以在不亲自实现IDispatch的情况下实现这一点。默认的IDispatch实现只对编译时已知的成员进行应答(它们必须在接口中定义)。