【问题标题】:How do I pass a C# interface to an external COM object for its use?如何将 C# 接口传递给外部 COM 对象以供其使用?
【发布时间】:2011-02-05 04:46:25
【问题描述】:

更新:

我发现 ITfDocumentMgr 的 IID 在注册表中,但 ITextStoreACP 的 IID 不在。当我从 ITextStoreACP 接口定义中删除 [Guid("28888....")] 时,Push() 方法没有崩溃,我怀疑是因为我的类没有报告它具有该接口。然后我把它放回去,使程序集、类和接口不是 COM 可见的。同样,我的 AdviseSink 方法没有被调用。

对此的任何想法将不胜感激。


接近原帖:

我正在尝试将文本服务框架集成到我的 C# 应用程序中,并将我的 C# 应用程序作为 TSF 客户端(而不是 TSF 服务)。我可以得到 TSF COM 接口来创建一个文档管理器好吗。我创建了一个实现 ITextStoreACP 的文本存储。

[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;
    }
....
}

创建使用 ITextStoreACP 的上下文后,我使用 DocumentMgr.Push() 来使用引用我的文本存储的上下文。反过来,TSF 框架应该调用我的 ITextStoreACP.AdviseSink() 方法及其接收器接口,以便我可以在文本存储内容发生更改时通知框架。

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
        {
        }

    }

}

我在这里使用 ITfDocumentMgr 接口定义:

    [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);
}

但是,应用程序没有收到对我的 AdviseSink() 方法的调用,而是在对 Push() 的调用中崩溃并关闭。如果我没有传递文本存储的接口(这是合法的),而是在调用 CreateContext 时传递一个 null,那么 Push() 会返回正常,因为没有 ITextStoreACP.AdviseSink() 可以调用。

我将项目属性“Make Assembly COM-Visible”设置为 true。 我将项目属性“Register for COM interop”设置为 true,但我认为这没有必要。

我还需要做些什么来使基于 ITextStoreACP 接口的 C# 类对象可供外部 COM 对象的方法访问以使其正确使用吗?

下面显示了我基于 TextStore 对象的接口。所有方法都在文本存储中实现,至少有默认的“方法未实现”异常,这样我就可以运行代码了。

    [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
        );
}

【问题讨论】:

  • 不,如果你只需要一个接口,这就是你需要做的!
  • 您是否为定义您需要实现的接口的 com dll(或 tlb)创建了互操作文件?您是否在 C# 项目中引用了上述互操作并从中继承了您的对象?

标签: c# com interface


【解决方案1】:

原来我需要以下签名:

[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
        );
    .......
}


public class TextStore : ITextStoreACP
{
    #region ITextStoreACP Members

    public uint AdviseSink(ref Guid Iid, object pUnknown, uint Mask)
    {
        throw new NotImplementedException();
    }
    .....
}

但是现在我发现了一个读取访问冲突,并且堆栈指针似乎在返回的路上弹出 0,这似乎表明调用本身仍然不太正确。

然后我发现我只需将 [PreserveSig] 添加到 AdviseSink 方法声明中,一切都很好....

... 或将 uint (HRESULT) 更改为 void(无返回码)。请参阅此处的链接:

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

不需要雇用任何人,只需要一点指导。谢谢大家!!!你太棒了!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 2011-01-19
    • 2010-12-02
    • 1970-01-01
    • 2012-08-29
    • 1970-01-01
    相关资源
    最近更新 更多