我正在为 Outlook 2010 开发 IM 提供程序。为此,我必须实现 Outlook 在启动时加载的一些 IMessenger* COM 接口。我想使用 C# 和一个进程外服务器来执行此操作,我在此处遵循了 MSDN 示例。
这是我已经实现的:
- 正确注册所有内容(CLSID、类型库等)并配置 Outlook,以便它在启动时尝试加载我的服务器
- 将 COM 服务器实现为本机 ATL/C++ 服务器并确保其正常工作
- 在 C#/.NET 中实现基本接口并确保已加载
现在,我基本上可以启动我的 .EXE 服务器,然后启动 Outlook 并观察我服务器的控制台窗口,同时它会显示来自 COM 子系统的所有方法调用(我已经为每个方法添加了日志记录)。问题是,在某些时候(仅在 .NET 实现中!)我在 Outlook 日志中遇到如下错误:
CMsoIMProviderOC20::HrEnsureServiceData !failed! Line: 402 hr = 0x8000FFFF
我确切地知道这在我的程序中发生的位置,但我对此无能为力(我已经尝试为此寻找解决方案好几天了)。请记住,如果我使用 C++/ATL 实现它,它实际上可以工作,所以我想它一定与 .NET 互操作封送处理的工作方式有关。
以下是详细信息:
基本上有3个接口与这个问题相关:IMessenger、IMessengerServices和IMessengerService:
[
uuid(D50C3186-0F89-48f8-B204-3604629DEE10), // IID_IMessenger
helpstring("Messenger Interface"),
helpcontext(0x0000),
dual,
oleautomation
]
interface IMessenger : IDispatch
{
...
[id(DISPID_MUAM_SERVICES), propget, helpstring("Returns services list."), helpcontext(0x0000)]
HRESULT Services([out, retval] IDispatch ** ppdispServices);
...
}
而我知道这个 Services 属性实际上应该返回这个接口:
[
uuid(2E50547B-A8AA-4f60-B57E-1F414711007B), // IID_IMessengerServices
helpstring("Messenger Services Interface"),
helpcontext(0x0000),
dual,
oleautomation
]
interface IMessengerServices : IDispatch
{
...
[id(DISPID_NEWENUM), propget, restricted, helpstring("Enumerates the services."), helpcontext(0x0000)]
HRESULT _NewEnum([out, retval] IUnknown **ppUnknown);
...
}
该接口反过来又有望返回 IMesengerService 接口。但是,这对于这个问题的目的并不重要。
在 C++/ATL 中,我像这样实现属性服务:
STDMETHOD(get_Services)(IDispatch ** ppdispServices)
{
(*ppdispServices) = (IDispatch*)new CComObject<CMessengerServices>();
(*ppdispServices)->AddRef();
return S_OK;
}
这是 C# 实现:
public object Services
{
get { return new MessengerServices(); }
}
直到这里,两种实现都一切正常!现在问题开始了……
首先奇怪的是,在 C++/ATL 中get__NewEnum(IUnknown **pUnkown)
,我的 CMessengerServices 类的函数永远不会被执行。而是调用其他函数。这很好。
然而,在 C# 中,调用的 MessengerServices 类的第一个成员是 GetEnumerator() 方法。我可以在那里设置一个断点,并清楚地看到它是 C# 代码中的最后一行(在我的控制下),它在 Outlook 停止启动并在其日志中报告 0x8000FFFF 错误之前执行。之后,我的 .EXE 服务器中没有其他代码行被调用。
MessengerServices 类实现有最重要的部分:
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true), Guid("DF394E2C-38E2-4B70-B707-9749A4F857B0")]
public class MessengerServices : IMessengerServices
{
IEnumerator IMessengerServices.GetEnumerator()
{
return messengerServices.GetEnumerator();
}
private readonly ArrayList messengerServices;
public MessengerServices()
{
messengerServices = new ArrayList { new MessengerService() };
}
...
}
这里是 tlbimp.exe 生成的 CCW 代理:
[ComVisible(true), Guid("2E50547B-A8AA-4F60-B57E-1F414711007B")]
public interface IMessengerServices : IEnumerable
{
[TypeLibFunc(TypeLibFuncFlags.FRestricted)]
[DispId(-4)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "System.Runtime.InteropServices.CustomMarshalers.EnumeratorToEnumVariantMarshaler, CustomMarshalers, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
new IEnumerator GetEnumerator();
...
}
最后一个提示是否可能有帮助:当我在 GetEnumerator() 实现中设置断点并按 F10 进行单步执行时,这是 Visual Studio 输出窗口中的最后一行:
跨过非用户代码'System.Runtime.InteropServices.CustomMarshalers.EnumeratorToEnumVariantMarshaler.MarshalManagedToNative
另一个有趣的事情(至少对我来说)是,如果我从接口声明和我的类定义中完全删除 Enumerator 东西,Outlook 仍然会调用我的接口,但使用另一种方法(为了完整起见,PrimaryService)但基本上有同样的错误。
老实说,我完全不知道如何处理这个错误?提前感谢您的任何帮助!