C# 和 IStream.Read

问题描述 投票:0回答:3

我正在尝试使用 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”,尽管“buffer”似乎包含有效数据。

c# istream
3个回答
8
投票

您应该为该参数传递一个指针。

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.FreeCoTaskMem()
来释放内存。


1
投票

这是一个基于汉斯答案的解决方案,为您提供一个可以直接放入项目中的课程。它为所有 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();

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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.