Question

I am developing an COM library that uses the IStream interface to read and write data. My MIDL code looks like this:

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

Since IStream and it's base interface ISequentialStream are not defined inside a type library, they get defined in mine. So far so good. However, when I view my type library with OLEView, ISequentialStream only defines the members RemoteRead and RemoteWrite, while I expected Read and Write, since they are what I am actually calling. Even more strange is, that the the MSDN lists those two members (additionally to the original ones), but states they are not supported.

The question

So what are those members and how do I use them from a client (e.g. a managed application to create a managed Stream wrapper for IStream)?

The long story

I want to implement a wrapper on the client side, that forwards IStream calls to .NET streams, like System.IO.FileStream. This wrapper could inherit from IStream like so:

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

And then, I want to call my server with this wrapper:

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

The problem is, that Lib.Stream in the previous example only provides RemoteRead and RemoteWrite, so that server calls to stream->Read() would end up in no mans land. As far as I understood, there is System.Runtime.InteropServices.ComTypes.IStream for managed COM servers, but in my example I have a unmanaged COM server and a managed client that should provide IStream instances.

Was it helpful?

Solution

Actually, there is no RemoteRead and RemoteWrite in ISequentialStream v-table layout. They exist only in ObjIdl.Idl, as an aid for RPC proxy/stub code generator. Have a look at ObjIdl.h from SDK:

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;

};

It's hard to guess why your type library ends up with RemoteRead/RemoteWrite names, instead of Read/Write. You may want to upload your IDL somewhere and post a link to it, if you need help with that.

However, as long as the v-table layout, the method signatures and the GUID of the interfaces from your typelib match those of ISequentialStream and IStream from ObjIdl.h, the method names do not matter.

Anyway, I would do as Igor suggested in his comment. Do not expose IStream at all in the type library. Use IUnknown in the IDL, and just cast it to System.Runtime.InteropServices.ComTypes.IStream inside the C# client's method implementation, when you actually do read/write, i.e.:

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

[UPDATE] I guess I see what's going on with the method names. Your situation is exactly like this:

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

Once more, I suggest to not drag non-automation compatible interfaces into the type library, and I'm not alone here with this advice. You actually drag a lot more unneeded stuff into your typlib, which projects to the C# side too. Stick with IUnknown and make your typelib neat. Or, at last, define your own binary/GUID compatible versions of them from scratch.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top