Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# and IStream.Read

Tags:

c#

istream

I'm trying to use System.Runtime.InteropServices.ComTypes.IStream from C#, but I'm having some trouble. According to MSDN, the C# definition looks like this:

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

Basically, I can read data from the stream, but the above "pcbRead" value is always "0" (even though the byte array contains my data). Doing some reading, it seems as if the pcbRead argument is somewhat tricky to set up properly (though I'm fairly new to C#).

Anyway, my code basically looks like this:

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

Again, the problem is that "myPtr" still contains "0" after the read, though "buffer" seems to contain valid data.

like image 820
Jeff Godfrey Avatar asked Dec 24 '09 16:12

Jeff Godfrey


1 Answers

You are supposed to pass a pointer for that argument. The IStream::Read() function will write the number of bytes that were actually read to the pointed-to location. This requires unsafe code in C#, for example:

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

Doing it without the unsafe keyword is possible too:

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

If you use this method only occasionally you ought to use Marshal.CoTaskMemFree() to release the memory.

like image 141
Hans Passant Avatar answered Sep 30 '22 01:09

Hans Passant