BitmapDownload.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / System / Windows / Media / Imaging / BitmapDownload.cs / 3 / BitmapDownload.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation
//
//  File: BitmapDownload.cs 
//
//----------------------------------------------------------------------------- 
#pragma warning disable 1634, 1691 // Allow suppression of certain presharp messages 

using System; 
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel; 
using System.ComponentModel.Design.Serialization;
using System.Reflection; 
using System.Threading; 
using System.Windows.Threading;
using MS.Internal; 
using System.Diagnostics;
using System.Windows.Media;
using System.Globalization;
using System.Security; 
using System.Security.Permissions;
using System.Runtime.InteropServices; 
using System.Windows.Media.Animation; 
using System.Windows.Media.Composition;
using System.Windows.Media.Imaging; 
using MS.Win32.PresentationCore;
using MS.Internal.AppModel;
using MS.Internal.PresentationCore;
using SR=MS.Internal.PresentationCore.SR; 
using SRID=MS.Internal.PresentationCore.SRID;
using System.Net; 
using System.Net.Cache; 
using System.Text;
using MS.Win32; 

namespace System.Windows.Media.Imaging
{
    #region QueueEntry 

    /// 
    /// QueueEntry 
    ///
    internal struct QueueEntry 
    {
        internal List decoders;
        internal Uri inputUri;
        internal Stream inputStream; 
        internal Stream outputStream;
        internal string streamPath; 
        internal byte[] readBuffer; 
        internal long contentLength;
        internal string contentType; 
        internal int lastPercent;
        internal WebRequest webRequest;
    }
 
    #endregion
 
    #region BitmapDownload 

    /// 
    /// The BitmapDownload class provides a way to download streams off-thread to
    /// a cache
    ///
    internal static class BitmapDownload 
    {
        ///  
        ///     Critical: This code initializes critical member queue 
        ///     TreatAsSafe: This code does not expose the critical data
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        static BitmapDownload()
        {
            _waitEvent = new AutoResetEvent(false); 

            _workQueue = Queue.Synchronized(new Queue()); 
 
            _uriTable = Hashtable.Synchronized(new Hashtable());
 
            _readCallback = new AsyncCallback(ReadCallback);

            _responseCallback = new AsyncCallback(ResponseCallback);
 
            _thread = new Thread(new ThreadStart(DownloadThreadProc));
 
            _syncLock = new object(); 

        } 
        #region Methods

        ///
        /// Begin a download 
        ///
        ///  
        ///     Critical: This code elevates to create a file and initiate download 
        /// 
        [SecurityCritical] 
        internal static void BeginDownload(
            BitmapDecoder decoder,
            Uri uri,
            RequestCachePolicy uriCachePolicy, 
            Stream stream
            ) 
        { 
            lock (_thread)
            { 
                if (!_thread.IsAlive)
                {
                    _thread.IsBackground = true;
                    _thread.Start(); 
                }
            } 
 
            QueueEntry entry;
 
            // If there is already a download for this uri, just add the decoder to the list
            if (uri != null)
            {
                lock (_syncLock) 
                {
                    if (_uriTable[uri] != null) 
                    { 
                        entry = (QueueEntry)_uriTable[uri];
                        entry.decoders.Add(decoder); 

                        return;
                    }
                } 
            }
 
            entry = new QueueEntry(); 
            entry.decoders  = new List();
 
            lock (_syncLock)
            {
                entry.decoders.Add(decoder);
            } 

            entry.inputUri = uri; 
            entry.inputStream = stream; 

            string cacheFolder = MS.Win32.WinInet.InternetCacheFolder.LocalPath; 

            string pathToUse = null;
            bool passed = false;
 
            (new FileIOPermission(FileIOPermissionAccess.Read |
                                  FileIOPermissionAccess.Write | 
                                  FileIOPermissionAccess.Append, cacheFolder)).Assert(); // BlessedAssert 
            try
            { 
                // Try creating a file 10000 (arbitrary) times
                for (int i = 0; i < 10000; i++)
                {
                    pathToUse = cacheFolder + (new Random().Next(10000, Int32.MaxValue)).ToString(CultureInfo.InvariantCulture); 
                    if (!File.Exists(pathToUse))
                    { 
                        try 
                        {
                            entry.outputStream = new FileStream( 
                                pathToUse,
                                FileMode.CreateNew,
                                FileAccess.ReadWrite
                                ); 
                            entry.streamPath = pathToUse;
                            passed = true; 
 
                            break;
                        } 
                        catch(Exception e)
                        {
                            if (CriticalExceptions.IsCriticalException(e))
                            { 
                                throw;
                            } 
 
                            continue;
                        } 
                    }
                }
            }
            finally 
            {
                FileIOPermission.RevertAssert(); 
            } 

            if (!passed) 
            {
                throw new IOException(SR.Get(SRID.Image_CannotCreateTempFile));
            }
 
            entry.readBuffer  = new byte[READ_SIZE];
            entry.contentLength = -1; 
            entry.contentType = string.Empty; 
            entry.lastPercent = 0;
 
            // Add the entry to the table if we know the uri
            if (uri != null)
            {
                lock (_syncLock) 
                {
                    _uriTable[uri] = entry; 
                } 
            }
 
            if (stream == null)
            {
                bool fElevate = false;
                if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) 
                {
                    SecurityHelper.BlockCrossDomainForHttpsApps(uri); 
 
                    // In this case we first check to see if the consumer has media permissions for
                    // safe media (Site of Origin + Cross domain), if it 
                    // does we assert and run the code that requires the assert
                    if (SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.NoAudio,
                                                                MediaPermissionVideo.NoVideo,
                                                                MediaPermissionImage.SafeImage)) 
                    {
                        fElevate = true; 
                    } 
                }
 
                // This is the case where we are accessing an http image from an http site and we have media permission
                if (fElevate)
                {
                    (new WebPermission(NetworkAccess.Connect, BindUriHelper.UriToString(uri))).Assert(); // BlessedAssert 
                }
                try 
                { 
                    entry.webRequest = WpfWebRequestHelper.CreateRequest(uri);
                    if (uriCachePolicy != null) 
                    {
                        entry.webRequest.CachePolicy = uriCachePolicy;
                    }
                } 
                finally
                { 
                    if(fElevate) 
                    {
                        WebPermission.RevertAssert(); 
                    }
                }

                entry.webRequest.BeginGetResponse(_responseCallback, entry); 
            }
            else 
            { 
                _workQueue.Enqueue(entry);
                // Signal 
                _waitEvent.Set();
            }
        }
 
        ///
        /// Thread Proc 
        /// 
        /// 
        ///     Critical: This code accesses the queue, extracts entries and reads content 
        ///     TreatAsSafe: This code does not expose the queue and the read from the stream are sent to a callback.
        /// 
        [SecurityCritical,SecurityTreatAsSafe]
        internal static void DownloadThreadProc() 
        {
            Queue workQueue = _workQueue; 
            for (;;) 
            {
                _waitEvent.WaitOne(); 

                while (workQueue.Count != 0)
                {
                    QueueEntry entry = (QueueEntry)workQueue.Dequeue(); 

                    #pragma warning disable 6500 
 
                    // Catch all exceptions and marshal them to the correct thread
                    try 
                    {
                        IAsyncResult result = entry.inputStream.BeginRead(
                            entry.readBuffer,
                            0, 
                            READ_SIZE,
                            _readCallback, 
                            entry 
                            );
                    } 
                    catch (Exception e)
                    {
                        MarshalException(entry, e);
                    } 

                    #pragma warning restore 6500 
                } 
            }
        } 

        ///
        /// Response callback
        ///  
        ///     Critical: This code accesses the queue and also calls into WebRequest methods
        ///  
        [SecurityCritical] 
        private static void ResponseCallback(IAsyncResult result)
        { 
            QueueEntry entry = (QueueEntry)result.AsyncState;

            #pragma warning disable 6500
 
            // Catch all exceptions and marshal them to the correct thread
            try 
            { 
                WebResponse response = WpfWebRequestHelper.EndGetResponse(entry.webRequest, result);
                entry.inputStream = response.GetResponseStream(); 
                entry.contentLength = response.ContentLength;
                entry.contentType = response.ContentType;
                entry.webRequest = null; // GC the WebRequest
                _workQueue.Enqueue(entry); 

                // Signal 
                _waitEvent.Set(); 
            }
            catch(Exception e) 
            {
                MarshalException(entry, e);
            }
 
            #pragma warning restore 6500
        } 
 
        ///
        /// Read callback 
        ///
        /// 
        ///     Critical: This code accesses the queue
        ///  
        [SecurityCritical]
        private static void ReadCallback(IAsyncResult result) 
        { 
            QueueEntry entry = (QueueEntry)result.AsyncState;
            int bytesRead = 0; 

            #pragma warning disable 6500

            try 
            {
                bytesRead = entry.inputStream.EndRead(result); 
            } 
            catch(Exception e)
            { 
                MarshalException(entry, e);
            }

            #pragma warning restore 6500 

            if (bytesRead == 0) 
            { 
                entry.outputStream.Flush();
                entry.outputStream.Seek(0, SeekOrigin.Begin); 


                lock (_syncLock)
                { 
                    // Fire download progress & completion event for each decoder
                    foreach (LateBoundBitmapDecoder decoder in entry.decoders) 
                    { 
                        //
                        // Marshal events to UI thread 
                        //
                        MarshalEvents(
                            decoder,
                            new DispatcherOperationCallback(decoder.ProgressCallback), 
                            100
                            ); 
 
                        MarshalEvents(
                            decoder, 
                            new DispatcherOperationCallback(decoder.DownloadCallback),
                            entry.outputStream
                            );
                    } 
                }
 
                // Delete entry from uri table 
                if (entry.inputUri != null)
                { 
                    lock (_syncLock)
                    {
                        _uriTable[entry.inputUri] = null;
                    } 
                }
            } 
            else 
            {
                entry.outputStream.Write(entry.readBuffer, 0, bytesRead); 

                if (entry.contentLength > 0)
                {
                    int percentComplete = (int)Math.Floor(100.0 * (double)entry.outputStream.Length / (double)entry.contentLength); 

                    // Only raise if percentage went up by ~1% (ie value changed). 
                    if (percentComplete != entry.lastPercent) 
                    {
                        // Update last value 
                        entry.lastPercent = percentComplete;

                        lock (_syncLock)
                        { 
                            // Fire download progress event for each decoder
                            foreach (LateBoundBitmapDecoder decoder in entry.decoders) 
                            { 
                                //
                                // Marshal events to UI thread 
                                //
                                MarshalEvents(
                                    decoder,
                                    new DispatcherOperationCallback(decoder.ProgressCallback), 
                                    percentComplete
                                    ); 
                            } 
                        }
                    } 
                }

                _workQueue.Enqueue(entry);
                _waitEvent.Set(); 
            }
        } 
 
        ///
        /// Marshals a call to the Dispatcher thread 
        ///
        private static void MarshalEvents(
            LateBoundBitmapDecoder decoder,
            DispatcherOperationCallback doc, 
            object arg
            ) 
        { 
            Dispatcher dispatcher = decoder.Dispatcher;
            if (dispatcher != null) 
            {
                dispatcher.BeginInvoke(DispatcherPriority.Normal, doc, arg);
            }
            else 
            {
                // Decoder seems to be thread free. This is probably bad 
                Debug.Assert(false); 
            }
        } 

        ///
        /// Marshal an exception to the Dispatcher thread
        /// 
        private static void MarshalException(
            QueueEntry entry, 
            Exception e 
            )
        { 
            lock (_syncLock)
            {
                // Fire download completion event for each decoder
                foreach (LateBoundBitmapDecoder decoder in entry.decoders) 
                {
                    MarshalEvents( 
                        decoder, 
                        new DispatcherOperationCallback(decoder.ExceptionCallback),
                        e 
                        );
                }

                if (entry.inputUri != null) 
                {
                    lock (_syncLock) 
                    { 
                        _uriTable[entry.inputUri] = null;
                    } 
                }
            }
        }
 
        #endregion
 
        #region Data Members 

        /// Thread event 
        internal static AutoResetEvent _waitEvent = new AutoResetEvent(false);

        /// Work Queue
        ///  
        ///     Critical: This element holds data that is obtained under an elevation
        ///  
        [SecurityCritical] 
        internal static Queue _workQueue;
 
        /// Uri hash table
        internal static Hashtable _uriTable;

        /// Read callback 
        internal static AsyncCallback _readCallback;
 
        /// Response callback 
        internal static AsyncCallback _responseCallback;
 
        /// Thread object
        private static Thread _thread;

        /// lock object 
        private static object _syncLock;
 
        /// Default async read size 
        private const int READ_SIZE = 1024;
 
        #endregion
    }

    #endregion 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK