Top / JUNK / ManagedIStream

System.Runtime.InteropServices.ComTypes.IStream を実装した System.IO.Stream のラッパークラスです。

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

[ComVisible(true)]
[Serializable]
public class ManagedIStream : IStream
{
    internal const int STREAM_SEEK_SET = 0x0;
    internal const int STREAM_SEEK_CUR = 0x1;
    internal const int STREAM_SEEK_END = 0x2;

    internal const int STGM_READ = 0x00000000;
    internal const int STGM_WRITE = 0x00000001;
    internal const int STGM_READWRITE = 0x00000002;

    internal const int STGTY_STORAGE = 1;
    internal const int STGTY_STREAM = 2;
    internal const int STGTY_LOCKBYTES = 3;
    internal const int STGTY_PROPERTY = 4;

    private readonly Stream _Stream;

    public ManagedIStream(Stream stream) {
        _Stream = stream;
    }

    public IntPtr GetComInterfaceForObject() {
        // .NET 4.5 まで
        return Marshal.GetComInterfaceForObject(this, typeof(IStream));
        // NET 4.5.1 からはこちら
        // return Marshal.GetComInterfaceForObject<ManagedIStream, IStream>(this);
    }

    public long Length {
        get {
            return _Stream.Length;
        }
    }

    public long Position {
        get {
            return _Stream.Position;
        }
        set {
            _Stream.Position = value;
        }
    }

    private static void WriteInt64(IntPtr lp, long value) {
        if (lp != IntPtr.Zero) {
            Marshal.WriteInt64(lp, value);
        }
    }

    public void Read(byte[] pv, int cb, IntPtr pcbRead) {
        int readed = _Stream.Read(pv, 0, cb);
        WriteInt64(pcbRead, readed);
    }

    public void Write(byte[] pv, int cb, IntPtr pcbWritten) {
        long cbWritten = WriteInternal(pv, cb);
        WriteInt64(pcbWritten, cbWritten);
    }

    private long WriteInternal(byte[] pv, int cb) {
        long start = _Stream.Position;
        _Stream.Write(pv, 0, cb);
        return _Stream.Position - start;
    }

    public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) {
        SeekOrigin seekOrigin;
        switch (dwOrigin) {
            case STREAM_SEEK_SET:
                seekOrigin = SeekOrigin.Begin;
                break;
            case STREAM_SEEK_CUR:
                seekOrigin = SeekOrigin.Current;
                break;
            case STREAM_SEEK_END:
                seekOrigin = SeekOrigin.End;
                break;
            default:
                throw new ArgumentOutOfRangeException("origin");
        }
        _Stream.Seek(dlibMove, seekOrigin);
        WriteInt64(plibNewPosition, _Stream.Position);
    }

    public void SetSize(long libNewSize) {
        _Stream.SetLength(libNewSize);
    }

    public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) {
        long totalRead = 0;
        long totalWrite = 0;
        const int bufferSize = 32767;
        byte[] buffer = new byte[bufferSize];
        while (cb > 0) {
            int readSize;
            if (cb > bufferSize) {
                readSize = bufferSize;
            } else {
                readSize = (int)cb;
            }
            int readed = _Stream.Read(buffer, 0, readSize);
            if (readed == 0) {
                throw new IOException();
            }
            totalRead += readed;
            cb -= readed;
            totalWrite += WriteIStream(pstm, buffer, readed);
        }
        WriteInt64(pcbRead, totalRead);
        WriteInt64(pcbWritten, totalWrite);
    }

    private long WriteIStream(IStream pstm, byte[] buffer, int writeSize) {
        IntPtr pcbWritten = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(long)));
        try {
            Marshal.WriteInt64(pcbWritten, 0L);
            pstm.Write(buffer, writeSize, pcbWritten);
            return Marshal.ReadInt64(pcbWritten);
        } finally {
            Marshal.FreeCoTaskMem(pcbWritten);
        }
    }

    public void Stat(out STATSTG pstatstg, int grfStatFlag) {
        int mode = 0;
        if (_Stream.CanRead && _Stream.CanWrite) {
            mode |= STGM_READWRITE;
        } else if (_Stream.CanRead) {
            mode |= STGM_READ;
        } else if (_Stream.CanWrite) {
            mode |= STGM_WRITE;
        } else {
            throw new IOException();
        }
        pstatstg = new STATSTG {
            type = STGTY_STREAM,
            cbSize = _Stream.Length,
            grfMode = mode
        };
    }

    void IStream.Commit(int grfCommitFlags) {
        throw new NotSupportedException();
    }

    void IStream.Revert() {
        throw new NotSupportedException();
    }

    void IStream.LockRegion(long libOffset, long cb, int dwLockType) {
        throw new NotSupportedException();
    }

    void IStream.UnlockRegion(long libOffset, long cb, int dwLockType) {
        throw new NotSupportedException();
    }

    public void Clone(out IStream ppstm) {
        var oldPosition = _Stream.Position;
        try {
            // インスタンス化してみる
            var cloneStream = (Stream)Activator.CreateInstance(_Stream.GetType());
            // ManagedIStream を作成
            ppstm = new ManagedIStream(cloneStream);
            // 先頭からコピー
            _Stream.Position = 0;
            CopyTo(ppstm, _Stream.Length, IntPtr.Zero, IntPtr.Zero);
            cloneStream.Position = 0;

        } catch {
            // エラーが発生したら未サポート
            throw new NotSupportedException();
        } finally {
            // Position を元に戻す
            _Stream.Position = oldPosition;
        }
    }
}



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2020-03-31 (火) 23:34:05 (56d)