2

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

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

由于IStream它的基本接口ISequentialStream未在类型库中定义,因此它们在我的中定义。到目前为止,一切都很好。但是,当我使用 OLEView 查看我的类型库时,ISequentialStream只定义了成员RemoteReadand RemoteWrite,而我期望Readand Write,因为它们是我实际调用的。更奇怪的是,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在前面的示例中只提供RemoteReadand RemoteWrite,因此服务器调用stream->Read()最终会进入无人区。据我了解,有System.Runtime.InteropServices.ComTypes.IStream托管 COM 服务器,但在我的示例中,我有一个非托管 COM 服务器和一个应该提供IStream实例的托管客户端。

4

1 回答 1

3

实际上,在v-table 布局中没有RemoteReadand 。它们仅存在于 中,作为 RPC 代理/存根代码生成器的辅助。从 SDK看一下:RemoteWriteISequentialStreamObjIdl.IdlObjIdl.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 与ISequentialStreamIStreamfrom匹配ObjIdl.h,方法名称就无关紧要。

无论如何,我会按照伊戈尔在他的评论中建议的那样做。不要IStream在类型库中公开。在 IDL 中使用IUnknown,并将其转换到System.Runtime.InteropServices.ComTypes.IStreamC# 客户端的方法实现内部,当您实际进行读/写时,即:

身份证:

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

再一次,我建议不要将非自动化兼容的接口拖到类型库中,而且我并不是唯一一个提出这个建议的人。实际上,您将更多不需要的东西拖到您的 typlib 中,它们也投射到 C# 端。坚持IUnknown并让你的 typelib 整洁。或者,最后,从头开始定义您自己的二进制/GUID 兼容版本。

于 2013-11-07T10:26:56.777 回答