ISequentialStream的RemoteRead和RemoteWrite成员是什么?

时间:2013-11-06 19:25:59

标签: com com-interop atl midl

我正在开发一个使用IStream接口读取和写入数据的COM库。我的MIDL代码如下所示:

interface IParser : IUnknown
{
    HRESULT Load([in] IStream* stream, [out, retval] IParsable** pVal);
};

由于IStream和它的基本接口ISequentialStream未在类型库中定义,因此它们在我的中定义。到现在为止还挺好。但是,当我使用OLEView查看我的类型库时,ISequentialStream仅定义成员RemoteReadRemoteWrite,而我期望ReadWrite,因为它们是我实际上在说什么。更奇怪的是,MSDN列出了这两个成员(除了原始成员之外),但表示他们不受支持。

问题

那些成员是什么以及如何从客户端使用它们(例如托管应用程序为Stream创建托管IStream包装器)?

长篇故事

我想在客户端实现一个包装器,它将IStream调用转发给.NET流,比如System.IO.FileStream。这个包装器可以继承IStream,如下所示:

public class Stream : Lib.IStream
{
    public System.IO.Stream BaseStream { get; private set; }

    public Stream(System.IO.Stream stream)
    {
        this.BaseStream = stream;
    }

    // All IStream members in here...
    public void Read(byte[] buffer, int bufferSize, IntPtr bytesReadPtr)
    {
         // further implementation...
         this.BaseStream.Read();
    }
}

然后,我想用这个包装器调用我的服务器:

var wrapper = new Stream(baseStream);
var parsable = parser.Load(wrapper);

问题在于,前一个示例中的Lib.Stream仅提供RemoteReadRemoteWrite,因此对stream->Read()的服务器调用最终将无人值守。据我所知,托管COM服务器有System.Runtime.InteropServices.ComTypes.IStream,但在我的示例中,我有一个非托管COM服务器和一个应该提供IStream个实例的托管客户端。

1 个答案:

答案 0 :(得分:3)

实际上,RemoteRead v-table布局中没有RemoteWriteISequentialStream。它们仅存在于ObjIdl.Idl中,作为RPC代理/存根代码生成器的辅助。从SDK中查看ObjIdl.h

MIDL_INTERFACE("0c733a30-2a1c-11ce-ade5-00aa0044773d")
ISequentialStream : public IUnknown
{
public:
    virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read( 
        /* [annotation] */ 
        __out_bcount_part(cb, *pcbRead)  void *pv,
        /* [in] */ ULONG cb,
        /* [annotation] */ 
        __out_opt  ULONG *pcbRead) = 0;

    virtual /* [local] */ HRESULT STDMETHODCALLTYPE Write( 
        /* [annotation] */ 
        __in_bcount(cb)  const void *pv,
        /* [in] */ ULONG cb,
        /* [annotation] */ 
        __out_opt  ULONG *pcbWritten) = 0;

};

很难猜测为什么您的类型库最终会使用RemoteRead / RemoteWrite个名称,而不是Read / Write。如果您需要帮助,可能需要在某处上传您的IDL并发布链接。

但是,只要v-table布局,类型库中的接口的方法签名和GUID与ISequentialStreamIStreamObjIdl.h的接口相匹配,方法名称无关紧要。

无论如何,我会像伊戈尔在评论中所说的那样做。不要在类型库中公开IStream。在IDL中使用IUnknown,当您实际执行读/写操作时,只需将其转换为C#客户端方法实现中的System.Runtime.InteropServices.ComTypes.IStream,即:

IDL:

interface IParser : IUnknown
{
    HRESULT Load([in] IUnknown* stream, [out, retval] IParsable** pVal);
};

C#:

IParsable Load(object stream)
{
    // ...
    var comStream = (System.Runtime.InteropServices.ComTypes.IStream)stream;
    comStream.Read(...);
    // ...
} 

[更新] 我想我看到方法名称发生了什么。你的情况完全是这样的:

https://groups.google.com/forum/#!topic/microsoft.public.vc.atl/e-qj0xwoVzg/discussion

再一次,我建议不要将non-automation compatible interfaces拖到类型库中,我并不是唯一有这个建议的人。实际上你将更多不必要的东西拖到你的typlib中,它也投射到C#端。坚持使用IUnknown并使你的typelib整洁。或者,最后,从头开始定义自己的二进制/ GUID兼容版本。