我正在开发一个使用IStream
接口读取和写入数据的COM库。我的MIDL代码如下所示:
interface IParser : IUnknown
{
HRESULT Load([in] IStream* stream, [out, retval] IParsable** pVal);
};
由于IStream
和它的基本接口ISequentialStream
未在类型库中定义,因此它们在我的中定义。到现在为止还挺好。但是,当我使用OLEView查看我的类型库时,ISequentialStream
仅定义成员RemoteRead
和RemoteWrite
,而我期望Read
和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
仅提供RemoteRead
和RemoteWrite
,因此对stream->Read()
的服务器调用最终将无人值守。据我所知,托管COM服务器有System.Runtime.InteropServices.ComTypes.IStream
,但在我的示例中,我有一个非托管COM服务器和一个应该提供IStream
个实例的托管客户端。
答案 0 :(得分:3)
实际上,RemoteRead
v-table布局中没有RemoteWrite
和ISequentialStream
。它们仅存在于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与ISequentialStream
中IStream
和ObjIdl.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兼容版本。