none
C#でITfThreadMgr2のインスタンスが作成できない RRS feed

  • 質問

  • 自作のエディターコンポーネントをストアアプリに移植しようとしています。「TSFに対応するには」というスレッドでTSFに対応する方法を聞いたところ、ITfThreadMgr2を使用すればいいという回答を貰いました。試しに以下のコードでインスタンスを作成してみたのですが、CreateDocumentMgr()を呼び出す段階でComExpectionが発生してしまいいます。どうすれば、インスタンス化できるんでしょうか。CoCreateInstance()をマーシャリングしても呼び出してみてもうまくいきませんでした

    >>型 'System.__ComObject' の COM オブジェクトをインターフェイス型 'DotNetTextStore.UnmanagedAPI.TSF.ITfDocumentMgr' にキャストできません。IID '{AA80E7F4-2021-11D2-93E0-0060B067B86E}' が指定されたインターフェイスの COM コンポーネント上での QueryInterface 呼び出しのときに次のエラーが発生したため、この操作に失敗しました: インターフェイスがサポートされていません (HRESULT からの例外: 0x80004002 (E_NOINTERFACE))。

    class TextStore
    {
    	ITfThreadMgr2 threadMgr;
    	ITfDocumentMgr _documentMgr;
    	public TextStore()
    	{
    		Type clsid = Marshal.GetTypeFromCLSID(TfDeclarations.CLSID_TF_ThreadMgr);
    		this.threadMgr = Activator.CreateInstance(clsid) as ITfThreadMgr2;
    		this.threadMgr.CreateDocumentMgr(out _documentMgr);
    	}
    }
        [ComImport,
         InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
         Guid("0ab198ef-6477-4ee8-8812-6780edb82d5e")
        ]
        public interface ITfThreadMgr2
        {
            /// <summary>
            /// ITfThreadMgr2::Activate
            /// </summary>
            /// <param name="clientId"></param>
            [SecurityCritical]
            void Activate(out int clientId);
    
            /// <summary>
            /// ITfThreadMgr2::ActivateEx
            /// </summary>
            /// <param name="clientId"></param>
            /// <param name="?"></param>
            [SecurityCritical]
            void ActivateEx(out int clientId,[In] TfNameFlags flags);
    
            /// <summary>
            /// ITfThreadMgr2::Deactivate
            /// </summary>
            [SecurityCritical]
            void Deactivate();
    
            /// <summary>
            /// ITfThreadMgr2::CreateDocumentMgr
            /// </summary>
            /// <param name="docMgr"></param>
            [SecurityCritical]
            void CreateDocumentMgr(out ITfDocumentMgr docMgr);
    
            /// <summary>
            /// ITfThreadMgr2::EnumDocumentMgrs
            /// </summary>
            /// <param name="enumDocMgrs"></param>
            void EnumDocumentMgrs(out IEnumTfDocumentMgrs enumDocMgrs);
            
            /// <summary>
            /// ITfThreadMgr2::GetFocus
            /// </summary>
            /// <param name="docMgr"></param>
            void GetFocus(out ITfDocumentMgr docMgr);
    
            /// <summary>
            /// ITfThreadMgr2::SetFocus
            /// </summary>
            /// <param name="docMgr"></param>
            [SecurityCritical]
            void SetFocus(ITfDocumentMgr docMgr);
    
            /// <summary>
            /// ITfThreadMgr2::IsThreadFocus
            /// </summary>
            /// <param name="isFocus"></param>
            void IsThreadFocus([MarshalAs(UnmanagedType.Bool)] out bool isFocus);
    
            /// <summary>
            /// ITfThreadMgr2::GetFunctionProvider
            /// </summary>
            /// <param name="classId"></param>
            /// <param name="funcProvider"></param>
            [SecurityCritical]
            void GetFunctionProvider(ref Guid classId, out ITfFunctionProvider funcProvider);
    
            /// <summary>
            /// ITfThreadMgr2::EnumFunctionProviders
            /// </summary>
            /// <param name="enumProviders"></param>
            void EnumFunctionProviders(out IEnumTfFunctionProviders enumProviders);
    
            /// <summary>
            /// ITfThreadMgr2::GetGlobalCompartment
            /// </summary>
            /// <param name="compartmentMgr"></param>
            [SecurityCritical]
            void GetGlobalCompartment(out ITfCompartmentMgr compartmentMgr);
    
            /// <summary>
            /// ITfThreadMgr2::GetActiveFlags
            /// </summary>
            /// <param name="flags"></param>
            void GetActiveFlags(out TfNameFlags flags);
    
            /// <summary>
            /// ITfThreadMgr2::ResumeKeystrokeHandling
            /// </summary>
            void ResumeKeystrokeHandling();
    
            /// <summary>
            /// ITfThreadMgr2::SuspendKeystrokeHandling
            /// </summary>
            void SuspendKeystrokeHandling();
        }

    2013年6月2日 13:00

回答

  • 試していませんが、一つ疑うポイントとして提示します。
    COM のインターフェースの並び順に意味があることはご存知でしょうか?

    どのような経緯でその順番の宣言にされたかはわかりませんが、手元の msctf.h によると、Activate, Deactivate, CreateDocumentMgr, EnumDocumentMgrs, ... となっています。
    しかし、あなたのその宣言は Activate, ActivateEx, Deactivate, CreateDocumentMgr, ... になっており、順番が違っています。
    (手元の C:\Program Files (x86)\Windows Kits\8.0\Include\um\msctf.h にて確認)

    .NET と違い、COM の世界では関数の順番に重要な意味があり、「n 番目の関数をこのような引数で呼ぶ」という形になります。
    あなたの宣言では、CreateDocumentMgr を呼んだつもりでも、実際は EnumDocumentMgrs を呼び出していると予想されます。
    そして、引数の数が一致しているため、呼び出し自体でエラーにならず、得られたオブジェクトのキャスト(QueryInterface)でエラーになります。

    COM のインターフェースを C# で書き写すのであれば、正確な原典(Windows SDK の ヘッダーファイルなど)を元に正しく変換する必要があります。

    2013年6月2日 14:06
    モデレータ

すべての返信

  • 試していませんが、一つ疑うポイントとして提示します。
    COM のインターフェースの並び順に意味があることはご存知でしょうか?

    どのような経緯でその順番の宣言にされたかはわかりませんが、手元の msctf.h によると、Activate, Deactivate, CreateDocumentMgr, EnumDocumentMgrs, ... となっています。
    しかし、あなたのその宣言は Activate, ActivateEx, Deactivate, CreateDocumentMgr, ... になっており、順番が違っています。
    (手元の C:\Program Files (x86)\Windows Kits\8.0\Include\um\msctf.h にて確認)

    .NET と違い、COM の世界では関数の順番に重要な意味があり、「n 番目の関数をこのような引数で呼ぶ」という形になります。
    あなたの宣言では、CreateDocumentMgr を呼んだつもりでも、実際は EnumDocumentMgrs を呼び出していると予想されます。
    そして、引数の数が一致しているため、呼び出し自体でエラーにならず、得られたオブジェクトのキャスト(QueryInterface)でエラーになります。

    COM のインターフェースを C# で書き写すのであれば、正確な原典(Windows SDK の ヘッダーファイルなど)を元に正しく変換する必要があります。

    2013年6月2日 14:06
    モデレータ
  • 手元で現象を再現させてみました。

    こういったインターフェースが正しい(はず)なのですが、EnumDocumentMgrs の戻り値を ITfDocumentMgr にわざと間違えさせて EnumDocumentMgrs を呼び出すと、同じ例外を引き起こせます。(GUID も一致します)

    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("0ab198ef-6477-4ee8-8812-6780edb82d5e")]
    private interface ITfThreadMgr2
    {
        int Activate();
        void Deactivate();
        ITfDocumentMgr CreateDocumentMgr();
        IEnumTfDocumentMgrs EnumDocumentMgrs();
        ITfDocumentMgr GotFocus();
        void SetFocus(ITfDocumentMgr documentManager);
        bool IsThreadFocus();
        ITfFunctionProvider GetFunctionProvider(Guid classId);
        ITfCompartmentMgr GetGlobalComparement();
        void ActivateEx(out int id, int flags);
        int GetActiveFlags();
        void SuspendKeystrokeHandling();
        void ResumeKeystrokeHandling();
    }
    

    なお、コードの正しさは保障しません。ご自身の責任でご対応ください。

    余談ですが、内部の関数宣言の戻り値が HRESULT で最後の引数が out の場合、.NET のインターフェースとしては上記の例のように戻り値にすることもできます。

    2013年6月2日 14:58
    モデレータ
  • ありがとうございます

    関数の順序をmsctf.hで書かれている通りにしたら正常に実行できました

    2013年6月2日 17:08