title: How do I pass a C# interface to an external COM object for its use?
body: <p>UPDATE:</p>
  
  <p>I found that the IID for ITfDocumentMgr was in the registry, but the IID for ITextStoreACP was not. When I removed the [Guid("28888....")] from my ITextStoreACP interface definition, the Push() method did not crash, I suspect because my class was not reporting it had that interface. I then placed it back and made the assembly, class, and interface NOT COM visible. Again, my AdviseSink method is not called. </p>
  
  <p>Any thoughts on this would be appreciated.</p>
  
  <hr>
  
  <p>NEAR ORIGINAL POST:</p>
  
  <p>I am trying to integrate Text Services Framework into my C# application, with my C# application as the TSF client (as opposed to a TSF service). I can get the TSF COM interfaces to create a document manager okay. I have created a text store that implements the ITextStoreACP.</p>
  
  <pre><code>[ComVisible(true)]
  public class TextStore : ITextStoreACP
  {
      public uint AdviseSink(
          [MarshalAs(UnmanagedType.Struct)] ref IID Iid,
          [MarshalAs(UnmanagedType.IUnknown)]  Object pUnknown,
          uint Mask
          )
      {
          ITextStoreAcpSink Sink = pUnknown as ITextStoreAcpSink;
          return 0;
      }
  ....
  }
  </code></pre>
  
  <p>After I create a context that uses my ITextStoreACP, I then use DocumentMgr.Push() to use the context that references my text store. In turn, the TSF framework should then call my ITextStoreACP.AdviseSink() method with its sink interface so that I can notify the framework when changes occur to the text store contents.</p>
  
  <pre><code>public partial class Form1 : Form
  {
      static ITextStoreACP pStore = new TextStore();
      static Object pIunknown;
  
      static ITfContext Context;
      static ITfDocumentMgr DocMgr;
      static IThreadMgr ThreadMgr;
  
  
      protected override void  OnLoad(EventArgs e)        
      {
          base.OnLoad(e);
  
          TfClientId Tid = new TfClientId();
          TfEditCookie Cookie = new TfEditCookie();
          uint hr;
  
  
          // Get a new thread manager...
          ThreadMgr = TsfInterfaces.CreateManager();
          // Activate the thread manager and get our cookie back...
          hr = ThreadMgr.Activate(out Tid);
          // Create a document manager for our use...
          hr = ThreadMgr.CreateDocumentMgr(out DocMgr);
  
          // Get the IUnknown interface to pass to CreateContext()...
          pIunknown = pStore as Object;
  
          // Create a context to work in, designating our text store for our use...
          hr = DocMgr.CreateContext(Tid, 0, pIunknown, out Context, out Cookie);
  
          try
          {
              // Select our context to work in now. With a text store as part of
              // the context, the Text services framework should call into our
              // text store object with its sink interface. 
              //
              //     But it crashes instead.
              //
              hr = DocMgr.Push(Context);
          }
          catch
          {
          }
  
      }
  
  }
  </code></pre>
  
  <p>I am using the ITfDocumentMgr interface definition here:</p>
  
  <pre><code>    [Guid("aa80e7f4-2021-11d2-93e0-0060b067b86e")]
  [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface ITfDocumentMgr 
  {
      uint CreateContext(
          TfClientId Tid, 
          uint Flags,
          [MarshalAs(UnmanagedType.IUnknown)] Object pUnkUnique,
          [MarshalAs(UnmanagedType.Interface)] out ITfContext pContext,
          out TfEditCookie pCookie
          );
  
      uint Push([MarshalAs(UnmanagedType.Interface)] ITfContext pContext);
  
      uint Pop(uint Flags);
  
      uint GetTop(out ITfContext Context);
  
      uint GetBase(out ITfContext Context);
  
      uint EnumContexts(out IEnumITfContexts EnumContexts);
  }
  </code></pre>
  
  <p>However, instead of receiving a call to my AdviseSink() method, the app crashes and closes from within the call to Push(). If I do not pass an interface for a text store (which is legal), but rather a null when I call CreateContext, then Push() returns fine, as there is no ITextStoreACP.AdviseSink() to call.</p>
  
  <p>I set the project property "Make Assembly COM-Visible" to true.
  I set the project property "Register for COM interop" to true, though I don't think this is necessary.</p>
  
  <p>Is there anything else I need to do to make my C# class object based upon the ITextStoreACP interface accessible to an external COM object's method for it to use correctly?</p>
  
  <p>The interface I base my TextStore object from is shown next. All methods are implemented in the text store, with at least the default "Method not implemented" exception, so that I can run the code.</p>
  
  <pre><code>    [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
          );
  
      uint UnadviseSink([MarshalAs(UnmanagedType.IUnknown)] object pUnknown);
  
      uint RequestLock(uint LockFlags, out uint hrSession);
  
      uint GetStatus([MarshalAs(UnmanagedType.Struct)] out TS_STATUS TSS);
  
  
      uint QueryInsert(long TestStart, long TestEnd, ulong Count, out long ResultStart, out long ResultEnd);
  
      uint GetSelection(
          ulong Index,
          ulong Count,
          [MarshalAs(UnmanagedType.Struct)] out TS_SELECTION_ACP Selection,
          out ulong Fetched
          );
  
      uint SetSelection( 
          ulong Count, 
          [MarshalAs(UnmanagedType.Struct)] ref TS_SELECTION_ACP Selection
          );
  
      uint GetText(
          long Start,
          long End,
          out string Plain,
          ulong PlainReq,
          out ulong PlainRet,
          [MarshalAs(UnmanagedType.Struct)] out TS_RUNINFO RunInfo,
          ulong RunInfoReq,
          out ulong RunInfoRet,
          out long Next
          );
  
      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
          );
  
      uint GetFormattedText(
          long Start, 
          long End, 
          [MarshalAs(UnmanagedType.Interface)] out IDataObject DataObject
          );
  
      uint GetEmbedded(
          long Pos,
          [MarshalAs(UnmanagedType.Struct)] ref IID GUID_Service,
          [MarshalAs(UnmanagedType.Struct)] ref IID IID_Service,
          [MarshalAs(UnmanagedType.IUnknown)] out object pUnk
          );
  
      uint QueryInsertEmbedded(
          [MarshalAs(UnmanagedType.Struct)] ref IID GUID_Service,
          [MarshalAs(UnmanagedType.Struct)] ref FORMATETC FormatEtc,
          [MarshalAs(UnmanagedType.Bool)] out bool IsInsertable
          );
  
      uint InsertEmbedded(
          uint Flags,
          long Start,
          long End,
          [MarshalAs(UnmanagedType.Interface)] IDataObject DataObject,
          [MarshalAs(UnmanagedType.Struct)] out TS_TEXTCHANCE Change
          );
  
      uint InsertTextAtSelection(
          uint Flags,
          string Text,
          ulong Count,
          out long Start,
          out long End,
          [MarshalAs(UnmanagedType.Struct)] out TS_TEXTCHANCE Change
          );
  
      uint InsertEmbeddedAtSelection(
          uint Flags,
          [MarshalAs(UnmanagedType.Interface)] IDataObject DataObject,
          out long Start,
          out long End,
          [MarshalAs(UnmanagedType.Struct)] out TS_TEXTCHANCE Change
          );
  
      uint RequestSupportedAttrs(
          uint Flags,
          ulong FilterAttrs,
          [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRID AttrId
          );
  
      uint RequestAttrsAtPosition(
          long Pos,
          ulong FilterAttrs,
          [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRID AttrId,
          uint Flags
          );
  
      uint RequestAttrsTransitioningAtPosition(
          long Pos,
          ulong FilterAttrs,
          [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRID AttrId,
          uint Flags
          );
  
      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
          );
  
      uint RetrieveRequestedAttrs(
          ulong Count,
          [MarshalAs(UnmanagedType.Struct)] ref TS_ATTRVAL Vals,
          out ulong Fetched
          );
  
      uint GetEndACP(out long Acp);
  
      uint GetActiveView(out TsViewCookie Cookie);
  
      uint GetACPFromPoint(
          TsViewCookie Cookie,
          [MarshalAs(UnmanagedType.Struct)] ref POINT Point,
          uint Flags,
          out long Acp
          );
  
      uint GetTextExt(
          TsViewCookie Cookie,
          long Start,
          long End,
          [MarshalAs(UnmanagedType.Struct)] out RECT Rect,
          out bool IsClipped
          );
  
      uint GetScreenExt(
          TsViewCookie Cookie,
          [MarshalAs(UnmanagedType.Struct)] out RECT Rect
          );
  
      uint GetWnd(
          TsViewCookie Cookie,
          [MarshalAs(UnmanagedType.I4)] IntPtr hWnd
          );
  }
  </code></pre>