Question

On Windows Phone 8 I have run in to an issue while reading files from the SD card using ExternalStorageFile.OpenForReadAsync(), which is the only way to read an SD card located file on Windows Phone.

Investigating further it seems that the Microsoft.Phone.Storage.NativeFileStream has a bug in it which means Seek and SetFilePointer don't work as they should. More detail is given here.

Does anyone have any suggestions how I can work-around this platform bug?

I thought maybe I could inherit from Microsoft.Phone.Storage.NativeFileStream and override the buggy methods, but NativeFileStream doesn't seem to be available and I'm not sure what the correct code should be anyway. Or perhaps I can force this Stream into my own Stream class where I can control these methods?

Perhaps I could pad the file I need to open with garbage at the beginning so I can start my seeks in the "higher 32bits of the long"? The file is specific to my app so it doesn't need to be opened by anything else.

Any ideas for a work-around? A bit of a lower level problem than I'm used to dealing with so keep to hear some ideas.

Was it helpful?

Solution

If the file is small then you can simply copy the stream to a MemoryStream:

Stream s = await file.OpenForReadAsync();
MemoryStream ms = new MemoryStream();
s.CopyTo(ms);

However, if the file is too large you'll run in to memory issues so the following stream wrapper class can be used to correct Microsoft's bug (though in future versions of Windows Phone you'll need to disable this fix once the bug has been fixed):

using System;
using System.IO;

namespace WindowsPhoneBugFix
{
    /// <summary>
    /// Stream wrapper to circumnavigate buggy Stream reading of stream returned by ExternalStorageFile.OpenForReadAsync()
    /// </summary>
    public sealed class ExternalStorageFileWrapper : Stream
    {
        private Stream _stream; // Underlying stream

        public ExternalStorageFileWrapper(Stream stream)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");

            _stream = stream;
        }

        // Workaround described here - http://stackoverflow.com/a/21538189/250254
        public override long Seek(long offset, SeekOrigin origin)
        {
            ulong uoffset = (ulong)offset;
            ulong fix = ((uoffset & 0xffffffffL) << 32) | ((uoffset & 0xffffffff00000000L) >> 32);
            return _stream.Seek((long)fix, origin);
        }

        public override bool CanRead
        {
            get { return _stream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return _stream.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return _stream.CanWrite; }
        }

        public override void Flush()
        {
            _stream.Flush();
        }

        public override long Length
        {
            get { return _stream.Length; }
        }

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

        public override int Read(byte[] buffer, int offset, int count)
        {
            return _stream.Read(buffer, offset, count);
        }

        public override void SetLength(long value)
        {
            _stream.SetLength(value);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            _stream.Write(buffer, offset, count);
        }
    }
}

Code is available here to drop in to your project: https://github.com/gavinharriss/ExternalStorageFileWrapper-wp8

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top