5

我正在尝试使用C# 中的System.Runtime.InteropServices.ComTypes.IStream,但遇到了一些麻烦。根据 MSDN,C# 定义如下所示:

void Read(
    byte[] pv,
    int cb,
    IntPtr pcbRead
)

基本上,我可以从流中读取数据,但上面的“pcbRead”值始终为“0”(即使字节数组包含我的数据)。做一些阅读,似乎 pcbRead 参数正确设置有点棘手(尽管我对 C# 相当陌生)。

无论如何,我的代码基本上是这样的:

myPtr = (IntPtr)0;
int buffSize = 8192;
byte[] buffer = new byte[buffSize];
while (true)
{
  strm.Read(buffer, buffSize, myPtr);
  fs.Write(buffer, 0, myPtr.ToInt32());
  if (myPtr.ToInt32() < buffSize) break;
}

同样,问题是“myPtr”在读取后仍然包含“0”,尽管“缓冲区”似乎包含有效数据。

4

3 回答 3

7

您应该为该参数传递一个指针。IStream::Read() 函数会将实际读取的字节数写入指向的位置。这需要 C# 中的不安全代码,例如:

unsafe static int Read(System.Runtime.InteropServices.ComTypes.IStream strm,
  byte[] buffer) {
  int bytesRead = 0;
  int* ptr = &bytesRead;
  strm.Read(buffer, buffer.Length, (IntPtr)ptr);
  return bytesRead;
}

在没有 unsafe 关键字的情况下也可以这样做:

private static IntPtr ReadBuffer;

static int Read(System.Runtime.InteropServices.ComTypes.IStream strm,
  byte[] buffer) {
  if (ReadBuffer == IntPtr.Zero) ReadBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)));
  strm.Read(buffer, buffer.Length, ReadBuffer);
  return Marshal.ReadInt32(ReadBuffer);
}

如果你只是偶尔使用这个方法,你应该使用 Marshal.CoTaskMemFree() 来释放内存。

于 2009-12-24T16:50:47.433 回答
1

这是一个基于 Hans 的答案的解决方案,它为您提供了一个您可以直接放入项目中的课程。它为所有 IStream 对象提供了扩展方法。

这会将数据传输到 .net 内存流,但如果您愿意,可以将其更改为文件。

using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace YourProject
{
  public static class IStreamExtensions
  {
    private const int bufferSize = 8192;
    public static MemoryStream ReadToMemoryStream(this IStream comStream)
    {
      var memoryStream = new MemoryStream();

      var amtRead = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)));
      Marshal.WriteInt32(amtRead, bufferSize);
      var buffer = new byte[bufferSize];
      while (Marshal.ReadInt32(amtRead) > 0)
      {
        comStream.Read(buffer, buffer.Length, amtRead);
        memoryStream.Write(buffer, 0, Marshal.ReadInt32(amtRead));
      }
      memoryStream.Position = 0;

      return memoryStream;
    }
  }
}

用法:

IStream istream = (IStream) someCOMclass.giveMeAStream();
MemoryStream netStream = istream.ReadToMemoryStream();
于 2016-05-25T19:40:35.923 回答
0

我对 IStream 没有经验,但是查看您的代码我发现了一些潜在的错误。
变量 myPtr 在开始时设置为零。IntPtr 像 C++ 中的指针一样工作,所以我认为这个方法期望它将值写入 myPtr 指向的位置。

你能尝试这样做吗?

unsafe 
{
    int pcbRead = 0;
    int buffSize = 8192;
    byte[] buffer = new byte[buffSize];
    while (true)
    {
        // taking address of pcbRead
        strm.Read(buffer, buffSize, new IntPtr(&pcbRead)); 
        fs.Write(buffer, 0, pcbRead);
        if (pcbRead < buffSize) break;
    }
}
于 2009-12-24T16:50:24.783 回答