Outlook 2010 用の IM プロバイダーを開発しています。そのためには、Outlook が起動時にロードする IMessenger* COM インターフェイスをいくつか実装する必要があります。C# と、MSDN の例hereに従ったアウト プロセス サーバーを使用してこれを実行したいと考えています。
これは私がすでに達成したことです:
- すべて (CLSID、タイプ ライブラリなど) を正しく登録し、起動時にサーバーを読み込もうとするように Outlook を構成します。
- COM サーバーをネイティブ ATL/C++ サーバーとして実装し、動作することを確認します
- C#/.NET で基本的なインターフェイスを実装し、それが読み込まれていることを確認します
今のところ、基本的に .EXE サーバーを起動してから Outlook を起動し、サーバーのコンソール ウィンドウを監視しながら、COM サブシステムからのすべてのメソッド呼び出しを表示できます (すべてのメソッドにログを追加しました)。問題は、ある時点で (.NET 実装でのみ!) Outlook ログに次のようなエラーが表示されることです。
CMsoIMProviderOC20::HrEnsureServiceData !failed! Line: 402 hr = 0x8000FFFF
これがプログラムのどこで発生するかは正確にわかっていますが、それについては何もできません (これに対する解決策を何日も探しました)。C++/ATL を使用して実装すると実際に動作するので、.NET Interop マーシャリングの動作に何らかの関係があるに違いないことを覚えておいてください。
詳細は次のとおりです。
基本的に、この問題に関連する 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 では、Services プロパティを次のように実装します。
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() メソッドです。そこにブレークポイントを設定すると、Outlook が起動を停止し、ログに 0x8000FFFF エラーを報告する前に実行される C# コード (私の制御下) の最後の行であることが明確にわかります。その後、.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
もう 1 つの興味深い点(少なくとも私にとって) は、Interface 宣言とクラス定義から Enumerator を完全に削除すると、Outlook は引き続き私のインターフェイスを呼び出しますが、代わりに別のメソッド (完全を期すために PrimaryService) を使用しますが、基本的には同じエラー。
正直なところ、このエラーをどうしたらいいのかまったくわかりません。どんな種類の助けにも感謝します!