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

ここでも問題は、"buffer" には有効なデータが含まれているように見えますが、読み取り後も "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

これは、ハンスの回答から構築されたソリューションであり、プロジェクトにドロップできるクラスを提供します。すべての ISt​​ream オブジェクトの拡張メソッドを提供します。

これにより、データが .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 に答える