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.
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.