none
Passing C# interface to COM client RRS feed

  • Question

  • I am trying to integrate TSF into my C# application. I wrote out some of the TSF COM interface signatures for my application to use when working with TSF.

    The standard use of TSF includes creating a thread manager, and from there creating a document manager. The second step returns to my C# app the ITfDocumentMgr interface, from which I create a context and use it.

    The call to ITfDocumentMgr.CreateContext() includes as a parameter the interface ITfTextStoreACP. This interface is defined by TSF, for which I merely create the interface signature in C# and use it on my TextStore object. When I create my TextStore in C#, I pass its interface to the document manager, a COM server on the system. This works fine. I get S_OK back and all looks good.

    Then I call ITfDocumentMgr.Push(), which should result in the TSF system calling a method in my TextStore using the ITfTextStoreACP interface I passed it. But it seems to crash at this point and my application simply quits suddenly.

    If I pass null for the ITfTextStoreACP interface, then the Push() method works fine. Of course, that is not going to do me any good, but it shows the problem is most likely in the TSF COM server using my C# interface as a COM interface.

    So I am wondering what I need to do to pass a C# interface to a COM server to use in its calling my C# class as a COM object for its use?

    I don't see how registering the C# class would do any good, as the TSF COM server itself is being handed an interface by my code already. (It would not need nor have any idea where to look for the interface it requires.)

    I specify my ITfTextStoreACP with the following attributes.

    [Guid("...")] // using the UUID of the ITfTextStoreACP interface as defined by Microsoft...
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ITfTextStoreACP
    {
      uint AdviseSink([MarshalAs(UnmanagedType.Struct)] ref IID Iid,
        [MarshalAs(UnmanagedType.IUnknown)] ref object pUnknown,
        uint Mask
        );
        ....
    }

    I specify my TextStore as follows.

    [ComVisible(true)] // doubt I need this
    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(ITfTextStoreACP))]
    public class TextStore : ITfTextStoreACP
    {
        public uint AdviseSink(ref IID Iid, ref object pUnknown, uint Mask)
        {
          ....
        }
        ...
    }

     

    Is there anything else I need to do so that my interface is marshaled correctly and used correctly by the TSF COM server?

    Any help would be much appreciated!!

     

     

    Tuesday, February 1, 2011 2:53 AM

Answers

  •  

     

    IT TURNS OUT THAT...!!!

    I needed the following signature:

     

    [ComVisible(true)]
        [Guid("28888fe3-c2a0-483a-a3ea-8cb1ce51ff3d")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface ITextStoreACP
        {
            uint AdviseSink(
                ref Guid Iid,
                [MarshalAs(UnmanagedType.IUnknown)] object pUnknown,
                uint Mask
                );

        ...

        }

     

    What is different between this and the original signature was that I had a ref for the pUnknown. When that is present, it crashed.

     

    Thanks Fabio, for being patient and helpful...

     

    But now it is crashing with a null pointer reference on the way back from AdviseSink. It looks like the stack pointer is loading itself with 0, which indicates that the call signature is still not quite right.

     

    UPDATE: It turns out that I needed to add [PreserveSig] to the AdviseSink()...

     

    ... or remove the HRESULT (uint) from the signature's return code. For more information, see...

    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.preservesigattribute%28v=VS.90%29.aspx
    http://social.msdn.microsoft.com/Forums/en-US/clr/thread/a8c2d872-a42e-441a-907b-62d4a05f75ea

    • Marked as answer by Greg Ofiesh Thursday, February 3, 2011 3:52 AM
    • Edited by Greg Ofiesh Friday, February 4, 2011 8:44 PM
    Thursday, February 3, 2011 3:52 AM

All replies

  • Have you completely declared the interface and implemented it? Can you pleas poste the complete interface declaration so I may try to find potential errors in the signatures?

    Regards,

    Fábio


    "To alcohol! The cause of and solution to all of life's problems." - Homer Simpson
    Tuesday, February 1, 2011 4:04 PM
  • Here is the interface that I am passing to CreateContext. I took the C++ file and commented it and then created each method signature above each C++ method. Every method is implemented with at least the default implementation that throws a method not implemented exception (or I would not be able to run it). I may have a couple things wrong, but from an earlier C++ implementation, I know that the only methods called are the IUnknown AddRef(), Release(), QueryInterface(), and the ITfTextStoreACP.AdviseSink(). I do not implement the IUnknown methods, as I assume they are handled for me by .NET. If I am wrong, I could sure use some help in understanding how to implement them correctly.

     

     


        [ComVisible(true)]
        [Guid("28888fe3-c2a0-483a-a3ea-8cb1ce51ff3d")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface ITextStoreACP
        {
            uint AdviseSink(
                [MarshalAs(UnmanagedType.Struct)] ref IID Iid,
                [MarshalAs(UnmanagedType.IUnknown)] object pUnknown,
                uint Mask
                );
    ////////virtual HRESULT STDMETHODCALLTYPE AdviseSink(
    ////////    /* [in] */ __RPC__in REFIID riid,
    ////////    /* [iid_is][in] */ __RPC__in_opt IUnknown *punk,
    ////////    /* [in] */ DWORD dwMask) = 0;
    ////////
            uint UnadviseSink([MarshalAs(UnmanagedType.IUnknown)] object pUnknown);
    ////////virtual HRESULT STDMETHODCALLTYPE UnadviseSink(
    ////////    /* [in] */ __RPC__in_opt IUnknown *punk) = 0;
    ////////
            uint RequestLock(uint LockFlags, out uint hrSession);
    ////////virtual HRESULT STDMETHODCALLTYPE RequestLock(
    ////////    /* [in] */ DWORD dwLockFlags,
    ////////    /* [out] */ __RPC__out HRESULT *phrSession) = 0;
    ////////
            uint GetStatus([MarshalAs(UnmanagedType.Struct)] out TS_STATUS TSS);
    ////////virtual HRESULT STDMETHODCALLTYPE GetStatus(
    ////////    /* [out] */ __RPC__out TS_STATUS *pdcs) = 0;
    ////////

            uint QueryInsert(long TestStart, long TestEnd, ulong Count, out long ResultStart, out long ResultEnd);
    ////////virtual HRESULT STDMETHODCALLTYPE QueryInsert(
    ////////    /* [in] */ LONG acpTestStart,
    ////////    /* [in] */ LONG acpTestEnd,
    ////////    /* [in] */ ULONG cch,
    ////////    /* [out] */ __RPC__out LONG *pacpResultStart,
    ////////    /* [out] */ __RPC__out LONG *pacpResultEnd) = 0;
    ////////
            uint GetSelection(
                ulong Index,
                ulong Count,
                [MarshalAs(UnmanagedType.Struct)] out TS_SELECTION_ACP Selection,
                out ulong Fetched
                );
    ////////virtual HRESULT STDMETHODCALLTYPE GetSelection(
    ////////    /* [in] */ ULONG ulIndex,
    ////////    /* [in] */ ULONG ulCount,
    ////////    /* [length_is][size_is][out] */ __RPC__out_ecount_part(ulCount, *pcFetched) TS_SELECTION_ACP *pSelection,
    ////////    /* [out] */ __RPC__out ULONG *pcFetched) = 0;
    ////////
            uint SetSelection(
                ulong Count,
                [MarshalAs(UnmanagedType.Struct)] ref TS_SELECTION_ACP Selection
                );
    ////////virtual HRESULT STDMETHODCALLTYPE SetSelection(
    ////////    /* [in] */ ULONG ulCount,
    ////////    /* [size_is][in] */ __RPC__in_ecount_full(ulCount) const TS_SELECTION_ACP *pSelection) = 0;
    ////////
            uint GetText(
                long Start,
                long End,
                out string Plain, // What kind of marhsaling should I specify here for "out WCHAR *" types?
                ulong PlainReq,
                out ulong PlainRet,
                [MarshalAs(UnmanagedType.Struct)] out TS_RUNINFO RunInfo,
                ulong RunInfoReq,
                out ulong RunInfoRet,
                out long Next
                );
    ////////virtual HRESULT STDMETHODCALLTYPE GetText(
    ////////    /* [in] */ LONG acpStart,
    ////////    /* [in] */ LONG acpEnd,
    ////////    /* [length_is][size_is][out] */ __RPC__out_ecount_part(cchPlainReq, *pcchPlainRet) WCHAR *pchPlain,
    ////////    /* [in] */ ULONG cchPlainReq,
    ////////    /* [out] */ __RPC__out ULONG *pcchPlainRet,
    ////////    /* [length_is][size_is][out] */ __RPC__out_ecount_part(cRunInfoReq, *pcRunInfoRet) TS_RUNINFO *prgRunInfo,
    ////////    /* [in] */ ULONG cRunInfoReq,
    ////////    /* [out] */ __RPC__out ULONG *pcRunInfoRet,
    ////////    /* [out] */ __RPC__out LONG *pacpNext) = 0;
    ////////
            uint SetText(
                uint Flags,
                long Start,
                long End,
                string Text, // What kind of marshaling should I specify here for "in WCHAR *" types?
                ulong Count,
                [MarshalAs(UnmanagedType.Struct)] out TS_TEXTCHANCE Change
                );
    ////////virtual HRESULT STDMETHODCALLTYPE SetText(
    ////////    /* [in] */ DWORD dwFlags,
    ////////    /* [in] */ LONG acpStart,
    ////////    /* [in] */ LONG acpEnd,
    ////////    /* [size_is][in] */ __RPC__in_ecount_full(cch) const WCHAR *pchText,
    ////////    /* [in] */ ULONG cch,
    ////////    /* [out] */ __RPC__out TS_TEXTCHANGE *pChange) = 0;
    ////////
            uint GetFormattedText(
                long Start,
                long End,
                [MarshalAs(UnmanagedType.Interface)] out IDataObject DataObject
                );
    ////////virtual HRESULT STDMETHODCALLTYPE GetFormattedText(
    ////////    /* [in] */ LONG acpStart,
    ////////    /* [in] */ LONG acpEnd,
    ////////    /* [out] */ __RPC__deref_out_opt IDataObject **ppDataObject) = 0;
    ////////
            uint GetEmbedded(
                long Pos,
                [MarshalAs(UnmanagedType.Struct)] ref IID GUID_Service,
                [MarshalAs(UnmanagedType.Struct)] ref IID IID_Service,
                [MarshalAs(UnmanagedType.IUnknown)] out object pUnk
                );
    ////////virtual HRESULT STDMETHODCALLTYPE GetEmbedded(
    ////////    /* [in] */ LONG acpPos,
    ////////    /* [in] */ __RPC__in REFGUID rguidService,
    ////////    /* [in] */ __RPC__in REFIID riid,
    ////////    /* [iid_is][out] */ __RPC__deref_out_opt IUnknown **ppunk) = 0;
    ////////
            uint QueryInsertEmbedded(
                [MarshalAs(UnmanagedType.Struct)] ref IID GUID_Service,
                [MarshalAs(UnmanagedType.Struct)] ref FORMATETC FormatEtc,
                [MarshalAs(UnmanagedType.Bool)] out bool IsInsertable
                );
    ////////virtual HRESULT STDMETHODCALLTYPE QueryInsertEmbedded(
    ////////    /* [in] */ __RPC__in const GUID *pguidService,
    ////////    /* [in] */ __RPC__in const FORMATETC *pFormatEtc,
    ////////    /* [out] */ __RPC__out BOOL *pfInsertable) = 0;
    ////////
            uint InsertEmbedded(
                uint Flags,
                long Start,
                long End,
                [MarshalAs(UnmanagedType.Interface)] IDataObject DataObject,
                [MarshalAs(UnmanagedType.Struct)] out TS_TEXTCHANCE Change
                );
    ////////virtual HRESULT STDMETHODCALLTYPE InsertEmbedded(
    ////////    /* [in] */ DWORD dwFlags,
    ////////    /* [in] */ LONG acpStart,
    ////////    /* [in] */ LONG acpEnd,
    ////////    /* [in] */ __RPC__in_opt IDataObject *pDataObject,
    ////////    /* [out] */ __RPC__out TS_TEXTCHANGE *pChange) = 0;
    ////////
            uint InsertTextAtSelection(
                uint Flags,
                string Text, // What kind of marshaling should I specify here for "in WCHAR *" types?
                ulong Count,
                out long Start,
                out long End,
                [MarshalAs(UnmanagedType.Struct)] out TS_TEXTCHANCE Change
                );
    ////////virtual HRESULT STDMETHODCALLTYPE InsertTextAtSelection(
    ////////    /* [in] */ DWORD dwFlags,
    ////////    /* [size_is][in] */ __RPC__in_ecount_full(cch) const WCHAR *pchText,
    ////////    /* [in] */ ULONG cch,
    ////////    /* [out] */ __RPC__out LONG *pacpStart,
    ////////    /* [out] */ __RPC__out LONG *pacpEnd,
    ////////    /* [out] */ __RPC__out TS_TEXTCHANGE *pChange) = 0;
    ////////
            uint InsertEmbeddedAtSelection(
                uint Flags,
                [MarshalAs(UnmanagedType.Interface)] IDataObject DataObject,
                out long Start,
                out long End,
                [MarshalAs(UnmanagedType.Struct)] out TS_TEXTCHANCE Change
                );
    ////////virtual HRESULT STDMETHODCALLTYPE InsertEmbeddedAtSelection(
    ////////    /* [in] */ DWORD dwFlags,
    ////////    /* [in] */ __RPC__in_opt IDataObject *pDataObject,
    ////////    /* [out] */ __RPC__out LONG *pacpStart,
    ////////    /* [out] */ __RPC__out LONG *pacpEnd,
    ////////    /* [out] */ __RPC__out TS_TEXTCHANGE *pChange) = 0;
    ////////
            uint RequestSupportedAttrs(
                uint Flags,
                ulong FilterAttrs,
                [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRID AttrId
                );
    ////////virtual HRESULT STDMETHODCALLTYPE RequestSupportedAttrs(
    ////////    /* [in] */ DWORD dwFlags,
    ////////    /* [in] */ ULONG cFilterAttrs,
    ////////    /* [unique][size_is][in] */ __RPC__in_ecount_full_opt(cFilterAttrs) const TS_ATTRID *paFilterAttrs) = 0;
    ////////
            uint RequestAttrsAtPosition(
                long Pos,
                ulong FilterAttrs,
                [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRID AttrId,
                uint Flags
                );
    ////////virtual HRESULT STDMETHODCALLTYPE RequestAttrsAtPosition(
    ////////    /* [in] */ LONG acpPos,
    ////////    /* [in] */ ULONG cFilterAttrs,
    ////////    /* [unique][size_is][in] */ __RPC__in_ecount_full_opt(cFilterAttrs) const TS_ATTRID *paFilterAttrs,
    ////////    /* [in] */ DWORD dwFlags) = 0;
    ////////
            uint RequestAttrsTransitioningAtPosition(
                long Pos,
                ulong FilterAttrs,
                [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRID AttrId,
                uint Flags
                );
    ////////virtual HRESULT STDMETHODCALLTYPE RequestAttrsTransitioningAtPosition(
    ////////    /* [in] */ LONG acpPos,
    ////////    /* [in] */ ULONG cFilterAttrs,
    ////////    /* [unique][size_is][in] */ __RPC__in_ecount_full_opt(cFilterAttrs) const TS_ATTRID *paFilterAttrs,
    ////////    /* [in] */ DWORD dwFlags) = 0;
    ////////
            uint FindNextAttrTransition(
                long Start,
                long Halt,
                ulong FilterAttrs,
                [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRID AttrId,
                uint Flags,
                out long Next,
                out bool Found,
                out long FoundOffset
                );
    ////////virtual HRESULT STDMETHODCALLTYPE FindNextAttrTransition(
    ////////    /* [in] */ LONG acpStart,
    ////////    /* [in] */ LONG acpHalt,
    ////////    /* [in] */ ULONG cFilterAttrs,
    ////////    /* [unique][size_is][in] */ __RPC__in_ecount_full_opt(cFilterAttrs) const TS_ATTRID *paFilterAttrs,
    ////////    /* [in] */ DWORD dwFlags,
    ////////    /* [out] */ __RPC__out LONG *pacpNext,
    ////////    /* [out] */ __RPC__out BOOL *pfFound,
    ////////    /* [out] */ __RPC__out LONG *plFoundOffset) = 0;
    ////////
            uint RetrieveRequestedAttrs(
                ulong Count,
                [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRVAL Vals,
                out ulong Fetched
                );
    ////////virtual HRESULT STDMETHODCALLTYPE RetrieveRequestedAttrs(
    ////////    /* [in] */ ULONG ulCount,
    ////////    /* [length_is][size_is][out] */ __RPC__out_ecount_part(ulCount, *pcFetched) TS_ATTRVAL *paAttrVals,
    ////////    /* [out] */ __RPC__out ULONG *pcFetched) = 0;
    ////////
            uint GetEndACP(out long Acp);
    ////////virtual HRESULT STDMETHODCALLTYPE GetEndACP(
    ////////    /* [out] */ __RPC__out LONG *pacp) = 0;
    ////////
            uint GetActiveView(out TsViewCookie Cookie);
    ////////virtual HRESULT STDMETHODCALLTYPE GetActiveView(
    ////////    /* [out] */ __RPC__out TsViewCookie *pvcView) = 0;
    ////////
            uint GetACPFromPoint(
                TsViewCookie Cookie,
                [MarshalAs(UnmanagedType.Struct)] ref POINT Point,
                uint Flags,
                out long Acp
                );
    ////////virtual HRESULT STDMETHODCALLTYPE GetACPFromPoint(
    ////////    /* [in] */ TsViewCookie vcView,
    ////////    /* [in] */ __RPC__in const POINT *ptScreen,
    ////////    /* [in] */ DWORD dwFlags,
    ////////    /* [out] */ __RPC__out LONG *pacp) = 0;
    ////////
            uint GetTextExt(
                TsViewCookie Cookie,
                long Start,
                long End,
                [MarshalAs(UnmanagedType.Struct)] out RECT Rect,
                out bool IsClipped
                );
    ////////virtual HRESULT STDMETHODCALLTYPE GetTextExt(
    ////////    /* [in] */ TsViewCookie vcView,
    ////////    /* [in] */ LONG acpStart,
    ////////    /* [in] */ LONG acpEnd,
    ////////    /* [out] */ __RPC__out RECT *prc,
    ////////    /* [out] */ __RPC__out BOOL *pfClipped) = 0;
    ////////
            uint GetScreenExt(
                TsViewCookie Cookie,
                [MarshalAs(UnmanagedType.Struct)] out RECT Rect
                );
    ////////virtual HRESULT STDMETHODCALLTYPE GetScreenExt(
    ////////    /* [in] */ TsViewCookie vcView,
    ////////    /* [out] */ __RPC__out RECT *prc) = 0;
    ////////
            uint GetWnd(
                TsViewCookie Cookie,
                [MarshalAs(UnmanagedType.I4)] IntPtr hWnd
                );
    ////////virtual HRESULT STDMETHODCALLTYPE GetWnd(
    ////////    /* [in] */ TsViewCookie vcView,
    ////////    /* [out] */ __RPC__deref_out_opt HWND *phwnd) = 0;
           
        }

    • Edited by Greg Ofiesh Thursday, February 3, 2011 4:08 AM
    Tuesday, February 1, 2011 8:55 PM
  • Ok, so let's chec for a few problems:

     

    On AdviseSink, you should remove ref from pUnknown, object is already passed by reference.

    Same for Unadvise sink.

    QueryInsert - out int ResultStart, out int ResultEnd should be long, not int .

    GetSelection - Index, Count and Fetched should be ulong, not uint.


    SetSelection - ulCount should be ulong, not uint.


    GetText - same long/int switch. For WCHAR you can use string.


    SetText - Same problems (HRESULT is correct though, keep uint return type)


    GetFormattedText - Same problem
              
    GetEmbedded - Same Problem

    QueryInsertEmbedded - for FORMATETC, use ref only if it's a struct


    InsertEmbedded - Same problems, plus remove ref from DataObject, it's already passed by reference.

     

    Ok, I think it's good enogh for now, since you can already notice that most mistakes are recurrent and should be the same in the rest. Please review your code taking into consideration the changes I'm suggesting, try again and let me know if you have mor doubts.

    Regards,

    Fábio


    "To alcohol! The cause of and solution to all of life's problems." - Homer Simpson
    Wednesday, February 2, 2011 12:56 PM
  • I assume DWORD remains uint. Made the changes, same problem still...

     

    This is clearly the correct direction for my needs (to integrate TSF into my C# application), but I just don't know enough about passing C# interfaces to an external COM object method to know what is going wrong here. I may be wrong, but I really suspect there is some type of attribute I am missing or something else related to passing the interface in a way that it can be properly used by the external COM object.

     

    UDPATE:

    So this is the latest of my discoveries. The IID for my ITextStoreACP interface was being published into the registry. When I changed that, the crashing stopped, but the object's methods per the interface are not called. When I included back the ComVisible(true) attribute, the crash occurred again. So I guess that the interface is not correct, but I cannot see how it is wrong. I am updating my list up above with the latest.

     

     

    Wednesday, February 2, 2011 7:48 PM
  •  

     

    IT TURNS OUT THAT...!!!

    I needed the following signature:

     

    [ComVisible(true)]
        [Guid("28888fe3-c2a0-483a-a3ea-8cb1ce51ff3d")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface ITextStoreACP
        {
            uint AdviseSink(
                ref Guid Iid,
                [MarshalAs(UnmanagedType.IUnknown)] object pUnknown,
                uint Mask
                );

        ...

        }

     

    What is different between this and the original signature was that I had a ref for the pUnknown. When that is present, it crashed.

     

    Thanks Fabio, for being patient and helpful...

     

    But now it is crashing with a null pointer reference on the way back from AdviseSink. It looks like the stack pointer is loading itself with 0, which indicates that the call signature is still not quite right.

     

    UPDATE: It turns out that I needed to add [PreserveSig] to the AdviseSink()...

     

    ... or remove the HRESULT (uint) from the signature's return code. For more information, see...

    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.preservesigattribute%28v=VS.90%29.aspx
    http://social.msdn.microsoft.com/Forums/en-US/clr/thread/a8c2d872-a42e-441a-907b-62d4a05f75ea

    • Marked as answer by Greg Ofiesh Thursday, February 3, 2011 3:52 AM
    • Edited by Greg Ofiesh Friday, February 4, 2011 8:44 PM
    Thursday, February 3, 2011 3:52 AM
  • That's great! But it's really weird that the IID structure didn't work, as it was implemented just like the documentation. Maybe we forgot some attribute applied to it. In any case, I'm glad you figured it out.

    And don't worry, questions like yours makes me learn more, so it's a two way street and I know your requirement is not of the easiest ones.

    Kind Regards,

    Fábio


    "To alcohol! The cause of and solution to all of life's problems." - Homer Simpson
    Thursday, February 3, 2011 12:35 PM