Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / MS / Internal / IO / Packaging / ByteRangeDownloader.cs / 1 / ByteRangeDownloader.cs
#if DEBUG #define TRACE #endif //---------------------------------------------------------------------------- // // File: ByteRangeDownloader.cs // // Description: // Downloader for downloading resources from the Internet in byte ranges using .NET // web requests. This class is used by ByteWrapper (unmanaged code) to make additional // web requests other than through WININET // // History: // 03/20/2003 - younggk Created // 06/30/2003 - younggk Ported to WCP tree // 10/25/2003 - brucemac Introduced FileMutex to enable safe shared access to the temp file // // Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.ComponentModel; // For Win32Exception using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.IsolatedStorage; // For IsolatedStorage temp file using System.Net; using System.Net.Cache; // For RequestCachePolicy using System.Runtime.InteropServices; // For Marshal using System.Security; // SecurityCritical, SecurityTreatAsSafe using System.Threading; // For Mutex using Microsoft.Win32.SafeHandles; using MS.Internal.PresentationCore; using System.Security.Permissions; // for WebPermission namespace MS.Internal.IO.Packaging { ////// Downloader for byte range requests /// // Consider (younggk 05/15/2003): For now, we will only process one batch of requests at a time. We will most likely // want to spin multiple threads and do multiple batches at a time in the future [FriendAccessAllowed] internal class ByteRangeDownloader : IDisposable { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Constructor for ByteRangeDownloader - for UrlMon usage /// /// event to signal when new data is available in tempStream /// uri we should make requests to /// temp file to write to ////// Critical /// 1) modifies Critical data _eventHandle /// [SecurityCritical] internal ByteRangeDownloader(Uri requestedUri, string tempFileName, SafeWaitHandle eventHandle) : this(requestedUri, eventHandle) { if (tempFileName == null) { throw new ArgumentNullException("tempFileName"); } if (tempFileName.Length <= 0) { throw new ArgumentException(SR.Get(SRID.InvalidTempFileName), "tempFileName"); } _tempFileStream = File.Open(tempFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); } ////// Constructor for ByteRangeDownloader - used by NetStream who owns the tempStream /// /// event to signal when new data is available in tempStream /// mutex to synchronize access to tempStream /// uri we should make requests to /// stream to write data to ////// Critical /// 1) modifies Critical data _eventHandle /// [SecurityCritical] internal ByteRangeDownloader(Uri requestedUri, Stream tempStream, SafeWaitHandle eventHandle, Mutex fileMutex) : this(requestedUri, eventHandle) { Debug.Assert(fileMutex != null, "FileMutex must be a valid mutex"); Debug.Assert(tempStream != null, "ByteRangeDownloader requires a stream to write to"); _tempFileStream = tempStream; _fileMutex = fileMutex; } #endregion Constructors //------------------------------------------------------ // // Interfaces // //----------------------------------------------------- #region IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this); // not strictly necessary, but if we ever have a subclass with a finalizer, this will be more efficient } ////// Critical /// 1) modifies Critical data _readEventHandle /// 2) modifies Critical data _proxy /// Safe /// 1) _eventHandle is Critical for set but we are just nulling it out for dispose /// 2) _proxy is Critical for set but safe to null for dispose /// [SecurityCritical, SecurityTreatAsSafe] protected virtual void Dispose(bool disposing) { if (disposing) { lock (_syncObject) { if (!_disposed) { try { // if there is no mutex, then we own the stream and we should close it if (FileMutex == null && _tempFileStream != null) { _tempFileStream.Close(); } } finally { _requestedUri = null; _byteRangesInProgress = null; _requestsOnWait = null; _byteRangesAvailable = null; _tempFileStream = null; _eventHandle = null; _proxy = null; _credentials = null; _cachePolicy = null; _disposed = true; } } } } } #endregion IDisposable //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Get the byte ranges that are downloaded /// ///byte ranges that are downloaded; byteRanges is one dimensional /// array consisting pairs of offset and length internal int[,] GetDownloadedByteRanges() { int[,] byteRanges = null; // The worker thread will never call dispose nor this method; no need lock CheckDisposed(); lock (_syncObject) { CheckErroredOutCondition(); int rangeCount = _byteRangesAvailable.Count / 2; // The worker thread will update the bytes downloaded; need to lock byteRanges = new int[rangeCount, 2]; for (int i = 0; i < rangeCount; ++i) { byteRanges[i, Offset_Index] = (int)_byteRangesAvailable[(i * 2) + Offset_Index]; byteRanges[i, Length_Index] = (int)_byteRangesAvailable[(i * 2) + Length_Index]; } _byteRangesAvailable.Clear(); } return byteRanges; } ////// Make byte range request /// /// byte ranges to be downloaded; byteRanges is two dimensional /// array consisting pairs of offset and length ////// Critical /// 1) Sets the callback function (ResponseCallback) which is critical /// Safe /// 1) Creates new CLR HttWebRequest where HttWebReponse is the caller of /// the callback function /// [SecurityCritical, SecurityTreatAsSafe] internal void RequestByteRanges(int[,] byteRanges) { // The worker thread will never call dispose nor this method; no need to lock CheckDisposed(); if (byteRanges == null) { throw new ArgumentNullException("byteRanges"); } CheckTwoDimensionalByteRanges(byteRanges); // When multiple byte ranges are requested through one HttpWebRequest, the current HttpWebResponse // returns a stream with headers which has to be manually parsed. At this point, we decided to // make only one byte range request at a time // At this point, none of the callers of this class will make more than one range // So, we will assert if more than one byte range request is made Debug.Assert(byteRanges.GetLength(0) == 1, "We don't support a request with multiple byte ranges"); _firstRequestMade = true; // If there is no request in progress, start the request process; otherwise put it in the wait queue lock (_syncObject) { CheckErroredOutCondition(); // _byteRangeInprogress can be cleared out from the worker thread and this method can be called // from the caller thread; need to lock if (_byteRangesInProgress == null) { _webRequest = CreateHttpWebRequest(byteRanges); _byteRangesInProgress = byteRanges; _webRequest.BeginGetResponse(ResponseCallback, this); } else // cannot make request yet, put the request in the wait queue { // Lazy Init if (_requestsOnWait == null) { // there are currently only ever two of these (one pair) // so optimize _requestsOnWait = new ArrayList(2); } for (int i = 0; i < byteRanges.GetLength(0); ++i) { // Add requests to the wait queue _requestsOnWait.Add((int) byteRanges[i, Offset_Index]); _requestsOnWait.Add((int) byteRanges[i, Length_Index]); } } } } ////// Convert bytes ranges from one dimensional array (pairs of offset and length) /// to two dimensional array /// /// byteRanges in one dimensional array consisting pairs of offset and length ///byteRanges in two dimensional array consisting pairs of offset and length static internal int[,] ConvertByteRanges(int[] inByteRanges) { CheckOneDimensionalByteRanges(inByteRanges); int[,] outByteRanges = new int[(inByteRanges.Length / 2),2]; for (int i=0, j=0; i < inByteRanges.Length; ++i, ++j) { outByteRanges[j,Offset_Index] = inByteRanges[i]; outByteRanges[j,Length_Index] = inByteRanges[i+1]; ++i; } return outByteRanges; } ////// Convert bytes ranges from two dimensional array (paris of offiset and length) /// to one dimensional array /// /// byteRanges in two dimensional array consisting pairs of offset and length ///byteRanges in one dimensional array consisting pairs of offset and length static internal int[] ConvertByteRanges(int[,] inByteRanges) { // Normallly we will check the input from the caller // but in our scenario, the two dimensional array is always generated by ByteRangeDownloader // No need to check #if DEBUG CheckTwoDimensionalByteRanges(inByteRanges); #endif int[] outByteRanges = new int[inByteRanges.Length]; for (int i=0, j=0; i < inByteRanges.GetLength(0); ++i, ++j) { outByteRanges[j] = inByteRanges[i, Offset_Index]; outByteRanges[++j] = inByteRanges[i, Length_Index]; } return outByteRanges; } #endregion Internal Methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties ////// Proxy for all requests to ByteRangeDownloader /// ////// Critical /// 1) accesses Critical member _proxy /// internal IWebProxy Proxy { [SecurityCritical] set { CheckDisposed(); if (value == null) { throw new ArgumentNullException("value"); } if (!_firstRequestMade) { _proxy = value; } else // Once first request is made it cannot change { throw new InvalidOperationException(SR.Get(SRID.RequestAlreadyStarted)); } } } ////// Authentication information for all requests to ByteRangeDownloader /// internal ICredentials Credentials { set { CheckDisposed(); _credentials = value; } } ////// Cache Policy for all requests to ByteRangeDownloader /// internal RequestCachePolicy CachePolicy { set { CheckDisposed(); if (!_firstRequestMade) { _cachePolicy = value; } else // Once first request is made it cannot cahnge { throw new InvalidOperationException(SR.Get(SRID.RequestAlreadyStarted)); } } } ////// OS synchronization object use to synchronize access to the temp file - if null, no [....] is needed /// internal Mutex FileMutex { get { CheckDisposed(); return _fileMutex; } } ////// Returns true if any request errored out /// internal bool ErroredOut { get { CheckDisposed(); lock (_syncObject) { return _erroredOut; } } } #endregion Internal Properties //----------------------------------------------------- // // Private Classes // //----------------------------------------------------- //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods ////// Check if the object is disposed /// ///this._disposed is not changed by a thread while another thread is calling this function private void CheckDisposed() { if (_disposed) { throw new ObjectDisposedException(null, SR.Get(SRID.ByteRangeDownloaderDisposed)); } } ////// Constructor for ByteRangeDownloader /// ////// Critical /// 1) modifies Critical data _eventHandle /// [SecurityCritical] private ByteRangeDownloader(Uri requestedUri, SafeWaitHandle eventHandle) { if (requestedUri == null) { throw new ArgumentNullException("requestedUri"); } // Ensure uri is correct scheme (http or https) Do case-sensitive comparison since Uri.Scheme contract is to return in lower case only. if (String.Compare(requestedUri.Scheme, Uri.UriSchemeHttp, StringComparison.Ordinal) != 0 && String.Compare(requestedUri.Scheme, Uri.UriSchemeHttps, StringComparison.Ordinal) != 0) { throw new ArgumentException(SR.Get(SRID.InvalidScheme), "requestedUri"); } if (eventHandle == null) { throw new ArgumentNullException("eventHandle"); } if (eventHandle.IsInvalid || eventHandle.IsClosed) { throw new ArgumentException(SR.Get(SRID.InvalidEventHandle), "eventHandle"); } _requestedUri = requestedUri; _eventHandle = eventHandle; } ////// Check if it has been errored out from the worker thread and re-throw the exception that was /// thrown from the worker thread /// ///No need to lock in this function since the caller always locks before making this call private void CheckErroredOutCondition() { if (_erroredOut) { throw new InvalidOperationException(SR.Get(SRID.ByteRangeDownloaderErroredOut), _erroredOutException); } } ////// Download the requested bytes /// ////// Critical /// 1) local assert of WebPermission to access get_Proxy property /// 2) accesses Critical member _proxy /// Safe /// 1) WebPermission assert is local and needed only to synchronize two WebRequest properties /// 2) Safe use of Critical member _proxy - used only to ensure that multiple WebRequests /// share the same proxy settings. Proxy member is known safe. /// [SecurityCritical, SecurityTreatAsSafe] private HttpWebRequest CreateHttpWebRequest(int[,] byteRanges) { HttpWebRequest request; // Create the request object request = (HttpWebRequest)WpfWebRequestHelper.CreateRequest(_requestedUri); request.ProtocolVersion = HttpVersion.Version11; request.Method = "GET"; // Set the Proxy to Empty one; If we don't set this to empty one, it will try to find one for us // and ends up triggering JScript in another assembly. This will throw PolicyException since the JScript // dll doesn't have execution right. This is bug in CLR; supposed to be fixed later // ToDo (younggk 05/15/2003): Need to keep consistent HTTP stack with the WININET one (e.g. authentication, proxy, cookies) // IWebProxy emptyProxy = GlobalProxySelection.GetEmptyWebProxy(); // request.Proxy = emptyProxy; // Local assert to allow Proxy get/set under partial trust new WebPermission(PermissionState.Unrestricted).Assert(); // Blessed try { request.Proxy = _proxy; } finally { WebPermission.RevertAssert(); } request.Credentials = _credentials; request.CachePolicy = _cachePolicy; // Add byte ranges (to header) for (int i = 0; i < byteRanges.GetLength(0); ++i) { request.AddRange(byteRanges[i,Offset_Index], byteRanges[i,Offset_Index] + byteRanges[i,Length_Index] - 1); } return request; } ////// Raise Win32 events informing a client that the requested bytes are available or it errored out /// /// indicates if an exception to be thrown on fail to set event ////// /// Critical /// 1) This method calls into SetEvent which is critical marked SUC /// 2) It accesses Critical data _eventHandle /// [SecurityCritical] private void RaiseEvent(bool throwExceptionOnError) { if (_eventHandle != null && !_eventHandle.IsInvalid && !_eventHandle.IsClosed) { if (MS.Win32.UnsafeNativeMethods.SetEvent(_eventHandle) == 0) { if (throwExceptionOnError) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } } } ////// ResponseCallBack /// /// async result ///static method not necessary ////// Critical /// 1) It calls RaiseEvent which is critical /// [SecurityCritical] private void ResponseCallback(IAsyncResult ar) { HttpWebResponse webResponse = null; lock (_syncObject) { try { if (_disposed) { return; } // The caller thread can dispose this class and the worker thread need to check the disposed // condition; need to lock // If disposed, there is nothing to handle webResponse = (HttpWebResponse)WpfWebRequestHelper.EndGetResponse(_webRequest, ar); // If it is not partial content, no need to look further if (webResponse.StatusCode == HttpStatusCode.PartialContent) { // // Check for few conditions // // Get the header and make sure that it was indeed the byte range response int beginOffset = _byteRangesInProgress[0, Offset_Index]; int endOffset = beginOffset+ _byteRangesInProgress[0,Length_Index] - 1; // HttpWebRequest in the current CLR does not allow multiple byte range requests. // At this point, none of the callers of this class will make more than one range at a time // So, we should not receive any response with more than one range returned // When multiple byte ranges requests are support in HttpWebRequest eventually, // there is a question on how to handle multipart response (Content-Type=multipart/byteranges) // At this point we only need to handle one byte range response (Content-Range header) only // Request was successful // Note: endOffset could be trimmed offset in the case where the response didn't // satisfy the entire request if (CheckContentRange(webResponse.Headers, beginOffset, ref endOffset)) { // Write out the bytes to the temp file if (WriteByteRange(webResponse, beginOffset, endOffset - beginOffset + 1)) { // The range is downloaded successfully; add it to the list _byteRangesAvailable.Add(beginOffset); _byteRangesAvailable.Add(endOffset - beginOffset + 1); } else _erroredOut = true; } else { _erroredOut = true; _erroredOutException = new NotSupportedException(SR.Get(SRID.ByteRangeRequestIsNotSupported)); } } else { _erroredOut = true; } } catch (Exception e) // catch (and re-throw) exceptions so we can inform the other thread { _erroredOut = true; _erroredOutException = e; throw e; } catch // catch (and re-throw) all kinds of exceptions so we can inform the other thread { // inform other thread of error condition _erroredOut= true; _erroredOutException = null; throw; } finally { if (webResponse != null) { webResponse.Close(); } // bytes requested are downloaded or errored out // inform the caller that these ranges are available RaiseEvent(!_erroredOut); } // If we haven't errored out already, process the next batch if (!_erroredOut) { ProcessWaitQueue(); } } } ////// Shared code for mutex and non-mutex to call /// private bool Write(Stream s, int offset, int length) { int readBytes; // Process the data chunk at a time (Size of the data that can be processed one time is WriteBufferSize) _tempFileStream.Seek(offset, SeekOrigin.Begin); while (length > 0) { // Read in the data into the buffer readBytes = s.Read(_buffer, 0, WriteBufferSize); if (readBytes == 0) break; // Write it out _tempFileStream.Write(_buffer, 0, readBytes); length -= readBytes; } if (length != 0) return false; _tempFileStream.Flush(); return true; } ////// Write out the downloaded byte ranges to the temp file /// /// Http web response /// Offset of the byte range /// Length of the byte range ///True if it is successfully written out private bool WriteByteRange(HttpWebResponse response, int offset, int length) { bool result = false; // Get the downloaded stream using (Stream s = response.GetResponseStream()) { if (_buffer == null) { _buffer = new byte[WriteBufferSize]; } // mutex available? if (_fileMutex != null) { // use it try { // block until temp file is available _fileMutex.WaitOne(); result = Write(s, offset, length); } finally { _fileMutex.ReleaseMutex(); } } else result = Write(s, offset, length); } return result; } ////// Process the requests that are in the wait queue /// ///This is only called from ResponseCallback which synchronize the call ////// Critical /// 1) Sets the callback function (ResponseCallback) which is critical /// Safe /// 1) Creates new CLR HttWebRequest where HttWebReponse is the caller of /// the callback function /// [SecurityCritical, SecurityTreatAsSafe] private void ProcessWaitQueue() { // There is other requests waiting in the queue; Process those if (_requestsOnWait != null && _requestsOnWait.Count > 0) { // _byteRangesInProgress is already allocated and can be reused _byteRangesInProgress[0,Offset_Index] = (int) _requestsOnWait[Offset_Index]; _byteRangesInProgress[0,Length_Index] = (int) _requestsOnWait[Length_Index]; _requestsOnWait.RemoveRange(0, 2); _webRequest = CreateHttpWebRequest(_byteRangesInProgress); _webRequest.BeginGetResponse(ResponseCallback, this); } else { // If there is nothing more to process in the wait queue, clear out // _byteRangesInProgress so that subsequent byte range requests can be made _byteRangesInProgress = null; } } ////// Check if the given byte ranges follows correct format /// 1) number of items in the array should be even number (It should be one dimensional array consisting pairs /// of offset and length /// 2) offset cannot be less than 0 /// 3) length cannot be less than equal to 0 /// /// Byte ranges to be checked ///True if the byte ranges are in correct format static private void CheckOneDimensionalByteRanges(int[] byteRanges) { // The byteRanges should never be less; perf optimization if (byteRanges.Length < 2 || (byteRanges.Length % 2) != 0) { throw new ArgumentException(SR.Get(SRID.InvalidByteRanges, "byteRanges")); } for (int i = 0; i < byteRanges.Length; i++) { if (byteRanges[i] < 0 || byteRanges[i+1] <= 0) { throw new ArgumentException(SR.Get(SRID.InvalidByteRanges, "byteRanges")); } i++; } } ////// Check if the given byte ranges follows correct format /// 1) array should consist pairs of offset and length /// 2) offset cannot be less than 0 /// 3) length cannot be less than equal to 0 /// /// Byte ranges to be checked ///True if the byte ranges are in correct format static private void CheckTwoDimensionalByteRanges(int[,] byteRanges) { if (byteRanges.GetLength(0) <= 0 || byteRanges.GetLength(1) != 2) { throw new ArgumentException(SR.Get(SRID.InvalidByteRanges, "byteRanges")); } for (int i = 0; i < byteRanges.GetLength(0); ++i) { if (byteRanges[i,Offset_Index] < 0 || byteRanges[i,Length_Index] <= 0) { throw new ArgumentException(SR.Get(SRID.InvalidByteRanges, "byteRanges")); } } } ////// Check if the some of byte range request is satisfied by the given response /// This method make sure of the following: /// 1) It contains Content-Range field /// 2) Content-Range follows the format defined in RFC2616 /// a. it should be in the form of "bytes 0-499/1234" or "bytes 0-499/*" /// b. it should not be "bytes */*" or "*/1234" /// c. last-byte-pos must not be less than its first byte pos /// 3) Ignore the response that does not conform to condition #1 and #2 /// /// collection of headers returned with WebResponse /// first byte pos the requested byte range /// last byte pos the requested byte range ///False if the response contains invalid Content-Range field /// or none of bytes requested is included in the response. /// True if the some bytes of the requested bytes are included in the response. static private bool CheckContentRange(WebHeaderCollection responseHeaders, int beginOffset, ref int endOffset) { String contentRange = responseHeaders[ContentRangeHeader]; // No Content-Range (condition #1) if (contentRange == null) { return false; } contentRange = contentRange.ToUpperInvariant(); // No Content-Range (condition #1) if (contentRange.Length == 0 || !contentRange.StartsWith(ByteRangeUnit, StringComparison.Ordinal)) { return false; } // ContentRange: BYTES XXX-YYY/ZZZ int index = contentRange.IndexOf('-'); if (index == -1) { return false; } // Get the first byte offset of the range (XXX) int firstByteOffset = Int32.Parse(contentRange.Substring(ByteRangeUnit.Length, index - ByteRangeUnit.Length), NumberStyles.None, NumberFormatInfo.InvariantInfo); contentRange = contentRange.Substring(index + 1); // ContentRange: YYY/ZZZ index = contentRange.IndexOf('/'); if (index == -1) { return false; } // Get the last byte offset of the range (YYY) int lastByteOffset = Int32.Parse(contentRange.Substring(0, index), NumberStyles.None, NumberFormatInfo.InvariantInfo); // Get the instance length // ContentRange: ZZZ contentRange = contentRange.Substring(index + 1); if (String.CompareOrdinal(contentRange, "*") != 0) { // Note: for firstByteOffset and lastByteOffset, we are using Int32.Parse to make sure Int32.Parse to throw // if it is not an integer or the integer is bigger than Int32 since HttpWebRequest.AddRange // only supports Int32 // Once HttpWebRequest.AddRange start supporting Int64 we should change it to Int64 and long Int32.Parse(contentRange, NumberStyles.None, NumberFormatInfo.InvariantInfo); } // The response is considered to be successful if // the last byte offset is greater than the first offset of the response // and the response range satisfies some or all of the requested range // However, we don't want to deal with the situation where the first byte of the response is not the beginning // of the requested range bool successful = (firstByteOffset <= lastByteOffset && beginOffset == firstByteOffset); // Check if the response didn't satisfy the end part of the requested range if (successful && lastByteOffset < endOffset) { endOffset = lastByteOffset; } return successful; } #endregion Private Methods //----------------------------------------------------- // // Private Properties // //------------------------------------------------------ //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields private bool _firstRequestMade; private bool _disposed; private Object _syncObject = new Object(); private bool _erroredOut; private Exception _erroredOutException; private Uri _requestedUri; // url to be downloaded private RequestCachePolicy _cachePolicy; ////// Critical /// 1) _proxy is Critical because we use it under Unrestricted assert /// [SecurityCritical] private IWebProxy _proxy; private ICredentials _credentials; private CookieContainer _cookieContainer = new CookieContainer(1); [SecurityCritical] private SafeWaitHandle _eventHandle; // event handle which needs to be raised to inform the caller that // the requested bytes are available private Mutex _fileMutex; // object controlling synchronization on the temp file - if this is null, we own the stream private System.IO.Stream _tempFileStream; // stream to write to private ArrayList _byteRangesAvailable = new ArrayList(2); // byte ranges that are downloaded private ArrayList _requestsOnWait; // List of byte ranges requested need to be processed private int[,] _byteRangesInProgress; private HttpWebRequest _webRequest; private byte[] _buffer; // Buffer used for writing out the downloaded bytes to temp file private const int WriteBufferSize = 4096; // Buffer size for writing out to the temp file private const int TimeOut = 5000; private const int Offset_Index = 0; private const int Length_Index = 1; private const String ByteRangeUnit = "BYTES "; private const String ContentRangeHeader = "Content-Range"; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. #if DEBUG #define TRACE #endif //---------------------------------------------------------------------------- // // File: ByteRangeDownloader.cs // // Description: // Downloader for downloading resources from the Internet in byte ranges using .NET // web requests. This class is used by ByteWrapper (unmanaged code) to make additional // web requests other than through WININET // // History: // 03/20/2003 - younggk Created // 06/30/2003 - younggk Ported to WCP tree // 10/25/2003 - brucemac Introduced FileMutex to enable safe shared access to the temp file // // Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.ComponentModel; // For Win32Exception using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.IsolatedStorage; // For IsolatedStorage temp file using System.Net; using System.Net.Cache; // For RequestCachePolicy using System.Runtime.InteropServices; // For Marshal using System.Security; // SecurityCritical, SecurityTreatAsSafe using System.Threading; // For Mutex using Microsoft.Win32.SafeHandles; using MS.Internal.PresentationCore; using System.Security.Permissions; // for WebPermission namespace MS.Internal.IO.Packaging { ////// Downloader for byte range requests /// // Consider (younggk 05/15/2003): For now, we will only process one batch of requests at a time. We will most likely // want to spin multiple threads and do multiple batches at a time in the future [FriendAccessAllowed] internal class ByteRangeDownloader : IDisposable { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Constructor for ByteRangeDownloader - for UrlMon usage /// /// event to signal when new data is available in tempStream /// uri we should make requests to /// temp file to write to ////// Critical /// 1) modifies Critical data _eventHandle /// [SecurityCritical] internal ByteRangeDownloader(Uri requestedUri, string tempFileName, SafeWaitHandle eventHandle) : this(requestedUri, eventHandle) { if (tempFileName == null) { throw new ArgumentNullException("tempFileName"); } if (tempFileName.Length <= 0) { throw new ArgumentException(SR.Get(SRID.InvalidTempFileName), "tempFileName"); } _tempFileStream = File.Open(tempFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); } ////// Constructor for ByteRangeDownloader - used by NetStream who owns the tempStream /// /// event to signal when new data is available in tempStream /// mutex to synchronize access to tempStream /// uri we should make requests to /// stream to write data to ////// Critical /// 1) modifies Critical data _eventHandle /// [SecurityCritical] internal ByteRangeDownloader(Uri requestedUri, Stream tempStream, SafeWaitHandle eventHandle, Mutex fileMutex) : this(requestedUri, eventHandle) { Debug.Assert(fileMutex != null, "FileMutex must be a valid mutex"); Debug.Assert(tempStream != null, "ByteRangeDownloader requires a stream to write to"); _tempFileStream = tempStream; _fileMutex = fileMutex; } #endregion Constructors //------------------------------------------------------ // // Interfaces // //----------------------------------------------------- #region IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this); // not strictly necessary, but if we ever have a subclass with a finalizer, this will be more efficient } ////// Critical /// 1) modifies Critical data _readEventHandle /// 2) modifies Critical data _proxy /// Safe /// 1) _eventHandle is Critical for set but we are just nulling it out for dispose /// 2) _proxy is Critical for set but safe to null for dispose /// [SecurityCritical, SecurityTreatAsSafe] protected virtual void Dispose(bool disposing) { if (disposing) { lock (_syncObject) { if (!_disposed) { try { // if there is no mutex, then we own the stream and we should close it if (FileMutex == null && _tempFileStream != null) { _tempFileStream.Close(); } } finally { _requestedUri = null; _byteRangesInProgress = null; _requestsOnWait = null; _byteRangesAvailable = null; _tempFileStream = null; _eventHandle = null; _proxy = null; _credentials = null; _cachePolicy = null; _disposed = true; } } } } } #endregion IDisposable //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Get the byte ranges that are downloaded /// ///byte ranges that are downloaded; byteRanges is one dimensional /// array consisting pairs of offset and length internal int[,] GetDownloadedByteRanges() { int[,] byteRanges = null; // The worker thread will never call dispose nor this method; no need lock CheckDisposed(); lock (_syncObject) { CheckErroredOutCondition(); int rangeCount = _byteRangesAvailable.Count / 2; // The worker thread will update the bytes downloaded; need to lock byteRanges = new int[rangeCount, 2]; for (int i = 0; i < rangeCount; ++i) { byteRanges[i, Offset_Index] = (int)_byteRangesAvailable[(i * 2) + Offset_Index]; byteRanges[i, Length_Index] = (int)_byteRangesAvailable[(i * 2) + Length_Index]; } _byteRangesAvailable.Clear(); } return byteRanges; } ////// Make byte range request /// /// byte ranges to be downloaded; byteRanges is two dimensional /// array consisting pairs of offset and length ////// Critical /// 1) Sets the callback function (ResponseCallback) which is critical /// Safe /// 1) Creates new CLR HttWebRequest where HttWebReponse is the caller of /// the callback function /// [SecurityCritical, SecurityTreatAsSafe] internal void RequestByteRanges(int[,] byteRanges) { // The worker thread will never call dispose nor this method; no need to lock CheckDisposed(); if (byteRanges == null) { throw new ArgumentNullException("byteRanges"); } CheckTwoDimensionalByteRanges(byteRanges); // When multiple byte ranges are requested through one HttpWebRequest, the current HttpWebResponse // returns a stream with headers which has to be manually parsed. At this point, we decided to // make only one byte range request at a time // At this point, none of the callers of this class will make more than one range // So, we will assert if more than one byte range request is made Debug.Assert(byteRanges.GetLength(0) == 1, "We don't support a request with multiple byte ranges"); _firstRequestMade = true; // If there is no request in progress, start the request process; otherwise put it in the wait queue lock (_syncObject) { CheckErroredOutCondition(); // _byteRangeInprogress can be cleared out from the worker thread and this method can be called // from the caller thread; need to lock if (_byteRangesInProgress == null) { _webRequest = CreateHttpWebRequest(byteRanges); _byteRangesInProgress = byteRanges; _webRequest.BeginGetResponse(ResponseCallback, this); } else // cannot make request yet, put the request in the wait queue { // Lazy Init if (_requestsOnWait == null) { // there are currently only ever two of these (one pair) // so optimize _requestsOnWait = new ArrayList(2); } for (int i = 0; i < byteRanges.GetLength(0); ++i) { // Add requests to the wait queue _requestsOnWait.Add((int) byteRanges[i, Offset_Index]); _requestsOnWait.Add((int) byteRanges[i, Length_Index]); } } } } ////// Convert bytes ranges from one dimensional array (pairs of offset and length) /// to two dimensional array /// /// byteRanges in one dimensional array consisting pairs of offset and length ///byteRanges in two dimensional array consisting pairs of offset and length static internal int[,] ConvertByteRanges(int[] inByteRanges) { CheckOneDimensionalByteRanges(inByteRanges); int[,] outByteRanges = new int[(inByteRanges.Length / 2),2]; for (int i=0, j=0; i < inByteRanges.Length; ++i, ++j) { outByteRanges[j,Offset_Index] = inByteRanges[i]; outByteRanges[j,Length_Index] = inByteRanges[i+1]; ++i; } return outByteRanges; } ////// Convert bytes ranges from two dimensional array (paris of offiset and length) /// to one dimensional array /// /// byteRanges in two dimensional array consisting pairs of offset and length ///byteRanges in one dimensional array consisting pairs of offset and length static internal int[] ConvertByteRanges(int[,] inByteRanges) { // Normallly we will check the input from the caller // but in our scenario, the two dimensional array is always generated by ByteRangeDownloader // No need to check #if DEBUG CheckTwoDimensionalByteRanges(inByteRanges); #endif int[] outByteRanges = new int[inByteRanges.Length]; for (int i=0, j=0; i < inByteRanges.GetLength(0); ++i, ++j) { outByteRanges[j] = inByteRanges[i, Offset_Index]; outByteRanges[++j] = inByteRanges[i, Length_Index]; } return outByteRanges; } #endregion Internal Methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties ////// Proxy for all requests to ByteRangeDownloader /// ////// Critical /// 1) accesses Critical member _proxy /// internal IWebProxy Proxy { [SecurityCritical] set { CheckDisposed(); if (value == null) { throw new ArgumentNullException("value"); } if (!_firstRequestMade) { _proxy = value; } else // Once first request is made it cannot change { throw new InvalidOperationException(SR.Get(SRID.RequestAlreadyStarted)); } } } ////// Authentication information for all requests to ByteRangeDownloader /// internal ICredentials Credentials { set { CheckDisposed(); _credentials = value; } } ////// Cache Policy for all requests to ByteRangeDownloader /// internal RequestCachePolicy CachePolicy { set { CheckDisposed(); if (!_firstRequestMade) { _cachePolicy = value; } else // Once first request is made it cannot cahnge { throw new InvalidOperationException(SR.Get(SRID.RequestAlreadyStarted)); } } } ////// OS synchronization object use to synchronize access to the temp file - if null, no [....] is needed /// internal Mutex FileMutex { get { CheckDisposed(); return _fileMutex; } } ////// Returns true if any request errored out /// internal bool ErroredOut { get { CheckDisposed(); lock (_syncObject) { return _erroredOut; } } } #endregion Internal Properties //----------------------------------------------------- // // Private Classes // //----------------------------------------------------- //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods ////// Check if the object is disposed /// ///this._disposed is not changed by a thread while another thread is calling this function private void CheckDisposed() { if (_disposed) { throw new ObjectDisposedException(null, SR.Get(SRID.ByteRangeDownloaderDisposed)); } } ////// Constructor for ByteRangeDownloader /// ////// Critical /// 1) modifies Critical data _eventHandle /// [SecurityCritical] private ByteRangeDownloader(Uri requestedUri, SafeWaitHandle eventHandle) { if (requestedUri == null) { throw new ArgumentNullException("requestedUri"); } // Ensure uri is correct scheme (http or https) Do case-sensitive comparison since Uri.Scheme contract is to return in lower case only. if (String.Compare(requestedUri.Scheme, Uri.UriSchemeHttp, StringComparison.Ordinal) != 0 && String.Compare(requestedUri.Scheme, Uri.UriSchemeHttps, StringComparison.Ordinal) != 0) { throw new ArgumentException(SR.Get(SRID.InvalidScheme), "requestedUri"); } if (eventHandle == null) { throw new ArgumentNullException("eventHandle"); } if (eventHandle.IsInvalid || eventHandle.IsClosed) { throw new ArgumentException(SR.Get(SRID.InvalidEventHandle), "eventHandle"); } _requestedUri = requestedUri; _eventHandle = eventHandle; } ////// Check if it has been errored out from the worker thread and re-throw the exception that was /// thrown from the worker thread /// ///No need to lock in this function since the caller always locks before making this call private void CheckErroredOutCondition() { if (_erroredOut) { throw new InvalidOperationException(SR.Get(SRID.ByteRangeDownloaderErroredOut), _erroredOutException); } } ////// Download the requested bytes /// ////// Critical /// 1) local assert of WebPermission to access get_Proxy property /// 2) accesses Critical member _proxy /// Safe /// 1) WebPermission assert is local and needed only to synchronize two WebRequest properties /// 2) Safe use of Critical member _proxy - used only to ensure that multiple WebRequests /// share the same proxy settings. Proxy member is known safe. /// [SecurityCritical, SecurityTreatAsSafe] private HttpWebRequest CreateHttpWebRequest(int[,] byteRanges) { HttpWebRequest request; // Create the request object request = (HttpWebRequest)WpfWebRequestHelper.CreateRequest(_requestedUri); request.ProtocolVersion = HttpVersion.Version11; request.Method = "GET"; // Set the Proxy to Empty one; If we don't set this to empty one, it will try to find one for us // and ends up triggering JScript in another assembly. This will throw PolicyException since the JScript // dll doesn't have execution right. This is bug in CLR; supposed to be fixed later // ToDo (younggk 05/15/2003): Need to keep consistent HTTP stack with the WININET one (e.g. authentication, proxy, cookies) // IWebProxy emptyProxy = GlobalProxySelection.GetEmptyWebProxy(); // request.Proxy = emptyProxy; // Local assert to allow Proxy get/set under partial trust new WebPermission(PermissionState.Unrestricted).Assert(); // Blessed try { request.Proxy = _proxy; } finally { WebPermission.RevertAssert(); } request.Credentials = _credentials; request.CachePolicy = _cachePolicy; // Add byte ranges (to header) for (int i = 0; i < byteRanges.GetLength(0); ++i) { request.AddRange(byteRanges[i,Offset_Index], byteRanges[i,Offset_Index] + byteRanges[i,Length_Index] - 1); } return request; } ////// Raise Win32 events informing a client that the requested bytes are available or it errored out /// /// indicates if an exception to be thrown on fail to set event ////// /// Critical /// 1) This method calls into SetEvent which is critical marked SUC /// 2) It accesses Critical data _eventHandle /// [SecurityCritical] private void RaiseEvent(bool throwExceptionOnError) { if (_eventHandle != null && !_eventHandle.IsInvalid && !_eventHandle.IsClosed) { if (MS.Win32.UnsafeNativeMethods.SetEvent(_eventHandle) == 0) { if (throwExceptionOnError) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } } } ////// ResponseCallBack /// /// async result ///static method not necessary ////// Critical /// 1) It calls RaiseEvent which is critical /// [SecurityCritical] private void ResponseCallback(IAsyncResult ar) { HttpWebResponse webResponse = null; lock (_syncObject) { try { if (_disposed) { return; } // The caller thread can dispose this class and the worker thread need to check the disposed // condition; need to lock // If disposed, there is nothing to handle webResponse = (HttpWebResponse)WpfWebRequestHelper.EndGetResponse(_webRequest, ar); // If it is not partial content, no need to look further if (webResponse.StatusCode == HttpStatusCode.PartialContent) { // // Check for few conditions // // Get the header and make sure that it was indeed the byte range response int beginOffset = _byteRangesInProgress[0, Offset_Index]; int endOffset = beginOffset+ _byteRangesInProgress[0,Length_Index] - 1; // HttpWebRequest in the current CLR does not allow multiple byte range requests. // At this point, none of the callers of this class will make more than one range at a time // So, we should not receive any response with more than one range returned // When multiple byte ranges requests are support in HttpWebRequest eventually, // there is a question on how to handle multipart response (Content-Type=multipart/byteranges) // At this point we only need to handle one byte range response (Content-Range header) only // Request was successful // Note: endOffset could be trimmed offset in the case where the response didn't // satisfy the entire request if (CheckContentRange(webResponse.Headers, beginOffset, ref endOffset)) { // Write out the bytes to the temp file if (WriteByteRange(webResponse, beginOffset, endOffset - beginOffset + 1)) { // The range is downloaded successfully; add it to the list _byteRangesAvailable.Add(beginOffset); _byteRangesAvailable.Add(endOffset - beginOffset + 1); } else _erroredOut = true; } else { _erroredOut = true; _erroredOutException = new NotSupportedException(SR.Get(SRID.ByteRangeRequestIsNotSupported)); } } else { _erroredOut = true; } } catch (Exception e) // catch (and re-throw) exceptions so we can inform the other thread { _erroredOut = true; _erroredOutException = e; throw e; } catch // catch (and re-throw) all kinds of exceptions so we can inform the other thread { // inform other thread of error condition _erroredOut= true; _erroredOutException = null; throw; } finally { if (webResponse != null) { webResponse.Close(); } // bytes requested are downloaded or errored out // inform the caller that these ranges are available RaiseEvent(!_erroredOut); } // If we haven't errored out already, process the next batch if (!_erroredOut) { ProcessWaitQueue(); } } } ////// Shared code for mutex and non-mutex to call /// private bool Write(Stream s, int offset, int length) { int readBytes; // Process the data chunk at a time (Size of the data that can be processed one time is WriteBufferSize) _tempFileStream.Seek(offset, SeekOrigin.Begin); while (length > 0) { // Read in the data into the buffer readBytes = s.Read(_buffer, 0, WriteBufferSize); if (readBytes == 0) break; // Write it out _tempFileStream.Write(_buffer, 0, readBytes); length -= readBytes; } if (length != 0) return false; _tempFileStream.Flush(); return true; } ////// Write out the downloaded byte ranges to the temp file /// /// Http web response /// Offset of the byte range /// Length of the byte range ///True if it is successfully written out private bool WriteByteRange(HttpWebResponse response, int offset, int length) { bool result = false; // Get the downloaded stream using (Stream s = response.GetResponseStream()) { if (_buffer == null) { _buffer = new byte[WriteBufferSize]; } // mutex available? if (_fileMutex != null) { // use it try { // block until temp file is available _fileMutex.WaitOne(); result = Write(s, offset, length); } finally { _fileMutex.ReleaseMutex(); } } else result = Write(s, offset, length); } return result; } ////// Process the requests that are in the wait queue /// ///This is only called from ResponseCallback which synchronize the call ////// Critical /// 1) Sets the callback function (ResponseCallback) which is critical /// Safe /// 1) Creates new CLR HttWebRequest where HttWebReponse is the caller of /// the callback function /// [SecurityCritical, SecurityTreatAsSafe] private void ProcessWaitQueue() { // There is other requests waiting in the queue; Process those if (_requestsOnWait != null && _requestsOnWait.Count > 0) { // _byteRangesInProgress is already allocated and can be reused _byteRangesInProgress[0,Offset_Index] = (int) _requestsOnWait[Offset_Index]; _byteRangesInProgress[0,Length_Index] = (int) _requestsOnWait[Length_Index]; _requestsOnWait.RemoveRange(0, 2); _webRequest = CreateHttpWebRequest(_byteRangesInProgress); _webRequest.BeginGetResponse(ResponseCallback, this); } else { // If there is nothing more to process in the wait queue, clear out // _byteRangesInProgress so that subsequent byte range requests can be made _byteRangesInProgress = null; } } ////// Check if the given byte ranges follows correct format /// 1) number of items in the array should be even number (It should be one dimensional array consisting pairs /// of offset and length /// 2) offset cannot be less than 0 /// 3) length cannot be less than equal to 0 /// /// Byte ranges to be checked ///True if the byte ranges are in correct format static private void CheckOneDimensionalByteRanges(int[] byteRanges) { // The byteRanges should never be less; perf optimization if (byteRanges.Length < 2 || (byteRanges.Length % 2) != 0) { throw new ArgumentException(SR.Get(SRID.InvalidByteRanges, "byteRanges")); } for (int i = 0; i < byteRanges.Length; i++) { if (byteRanges[i] < 0 || byteRanges[i+1] <= 0) { throw new ArgumentException(SR.Get(SRID.InvalidByteRanges, "byteRanges")); } i++; } } ////// Check if the given byte ranges follows correct format /// 1) array should consist pairs of offset and length /// 2) offset cannot be less than 0 /// 3) length cannot be less than equal to 0 /// /// Byte ranges to be checked ///True if the byte ranges are in correct format static private void CheckTwoDimensionalByteRanges(int[,] byteRanges) { if (byteRanges.GetLength(0) <= 0 || byteRanges.GetLength(1) != 2) { throw new ArgumentException(SR.Get(SRID.InvalidByteRanges, "byteRanges")); } for (int i = 0; i < byteRanges.GetLength(0); ++i) { if (byteRanges[i,Offset_Index] < 0 || byteRanges[i,Length_Index] <= 0) { throw new ArgumentException(SR.Get(SRID.InvalidByteRanges, "byteRanges")); } } } ////// Check if the some of byte range request is satisfied by the given response /// This method make sure of the following: /// 1) It contains Content-Range field /// 2) Content-Range follows the format defined in RFC2616 /// a. it should be in the form of "bytes 0-499/1234" or "bytes 0-499/*" /// b. it should not be "bytes */*" or "*/1234" /// c. last-byte-pos must not be less than its first byte pos /// 3) Ignore the response that does not conform to condition #1 and #2 /// /// collection of headers returned with WebResponse /// first byte pos the requested byte range /// last byte pos the requested byte range ///False if the response contains invalid Content-Range field /// or none of bytes requested is included in the response. /// True if the some bytes of the requested bytes are included in the response. static private bool CheckContentRange(WebHeaderCollection responseHeaders, int beginOffset, ref int endOffset) { String contentRange = responseHeaders[ContentRangeHeader]; // No Content-Range (condition #1) if (contentRange == null) { return false; } contentRange = contentRange.ToUpperInvariant(); // No Content-Range (condition #1) if (contentRange.Length == 0 || !contentRange.StartsWith(ByteRangeUnit, StringComparison.Ordinal)) { return false; } // ContentRange: BYTES XXX-YYY/ZZZ int index = contentRange.IndexOf('-'); if (index == -1) { return false; } // Get the first byte offset of the range (XXX) int firstByteOffset = Int32.Parse(contentRange.Substring(ByteRangeUnit.Length, index - ByteRangeUnit.Length), NumberStyles.None, NumberFormatInfo.InvariantInfo); contentRange = contentRange.Substring(index + 1); // ContentRange: YYY/ZZZ index = contentRange.IndexOf('/'); if (index == -1) { return false; } // Get the last byte offset of the range (YYY) int lastByteOffset = Int32.Parse(contentRange.Substring(0, index), NumberStyles.None, NumberFormatInfo.InvariantInfo); // Get the instance length // ContentRange: ZZZ contentRange = contentRange.Substring(index + 1); if (String.CompareOrdinal(contentRange, "*") != 0) { // Note: for firstByteOffset and lastByteOffset, we are using Int32.Parse to make sure Int32.Parse to throw // if it is not an integer or the integer is bigger than Int32 since HttpWebRequest.AddRange // only supports Int32 // Once HttpWebRequest.AddRange start supporting Int64 we should change it to Int64 and long Int32.Parse(contentRange, NumberStyles.None, NumberFormatInfo.InvariantInfo); } // The response is considered to be successful if // the last byte offset is greater than the first offset of the response // and the response range satisfies some or all of the requested range // However, we don't want to deal with the situation where the first byte of the response is not the beginning // of the requested range bool successful = (firstByteOffset <= lastByteOffset && beginOffset == firstByteOffset); // Check if the response didn't satisfy the end part of the requested range if (successful && lastByteOffset < endOffset) { endOffset = lastByteOffset; } return successful; } #endregion Private Methods //----------------------------------------------------- // // Private Properties // //------------------------------------------------------ //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields private bool _firstRequestMade; private bool _disposed; private Object _syncObject = new Object(); private bool _erroredOut; private Exception _erroredOutException; private Uri _requestedUri; // url to be downloaded private RequestCachePolicy _cachePolicy; ////// Critical /// 1) _proxy is Critical because we use it under Unrestricted assert /// [SecurityCritical] private IWebProxy _proxy; private ICredentials _credentials; private CookieContainer _cookieContainer = new CookieContainer(1); [SecurityCritical] private SafeWaitHandle _eventHandle; // event handle which needs to be raised to inform the caller that // the requested bytes are available private Mutex _fileMutex; // object controlling synchronization on the temp file - if this is null, we own the stream private System.IO.Stream _tempFileStream; // stream to write to private ArrayList _byteRangesAvailable = new ArrayList(2); // byte ranges that are downloaded private ArrayList _requestsOnWait; // List of byte ranges requested need to be processed private int[,] _byteRangesInProgress; private HttpWebRequest _webRequest; private byte[] _buffer; // Buffer used for writing out the downloaded bytes to temp file private const int WriteBufferSize = 4096; // Buffer size for writing out to the temp file private const int TimeOut = 5000; private const int Offset_Index = 0; private const int Length_Index = 1; private const String ByteRangeUnit = "BYTES "; private const String ContentRangeHeader = "Content-Range"; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- MobileUserControlDesigner.cs
- InlineUIContainer.cs
- mda.cs
- SqlReorderer.cs
- OperationCanceledException.cs
- TypeConverterHelper.cs
- UInt64.cs
- Interlocked.cs
- SyntaxCheck.cs
- RadioButton.cs
- SettingsAttributes.cs
- WebPartConnectionsConnectVerb.cs
- COSERVERINFO.cs
- XmlReaderSettings.cs
- DataServiceSaveChangesEventArgs.cs
- TdsParserStaticMethods.cs
- PermissionSetEnumerator.cs
- DataBinder.cs
- DrawingImage.cs
- EventLogEntry.cs
- WrappedKeySecurityToken.cs
- DataRowView.cs
- WebPartZoneBase.cs
- CqlWriter.cs
- LocalizabilityAttribute.cs
- XmlJsonReader.cs
- HttpApplicationStateBase.cs
- IdentifierService.cs
- AuthenticationManager.cs
- CompoundFileStreamReference.cs
- Registry.cs
- WorkflowNamespace.cs
- ToolStripItemBehavior.cs
- DataList.cs
- Pen.cs
- OSEnvironmentHelper.cs
- WindowsStartMenu.cs
- CryptoStream.cs
- EraserBehavior.cs
- DbConnectionOptions.cs
- EntityModelSchemaGenerator.cs
- ServicePerformanceCounters.cs
- DataGridViewColumn.cs
- Random.cs
- Pointer.cs
- RegexCapture.cs
- DesignerSerializationManager.cs
- SqlDataSourceView.cs
- ClientSideQueueItem.cs
- PostBackOptions.cs
- EditModeSwitchButton.cs
- InkSerializer.cs
- GACMembershipCondition.cs
- CodeNamespaceImport.cs
- KnownAssembliesSet.cs
- SmiEventStream.cs
- DecoderReplacementFallback.cs
- BaseTemplateBuildProvider.cs
- InternalTypeHelper.cs
- __ConsoleStream.cs
- EdgeProfileValidation.cs
- Win32NamedPipes.cs
- LinqDataSourceValidationException.cs
- XmlSchemaExporter.cs
- StringConverter.cs
- Figure.cs
- TableColumn.cs
- odbcmetadatacollectionnames.cs
- ObjectDataSourceSelectingEventArgs.cs
- TypeDescriptor.cs
- TargetInvocationException.cs
- AVElementHelper.cs
- NavigationWindow.cs
- PerfCounterSection.cs
- VirtualDirectoryMapping.cs
- PinProtectionHelper.cs
- ProcessHostMapPath.cs
- SqlBuffer.cs
- DataGrid.cs
- ControlType.cs
- DefaultProxySection.cs
- ProfilePropertyNameValidator.cs
- CodeDesigner.cs
- PropertyOrder.cs
- MessageBox.cs
- HotSpotCollection.cs
- WinFormsUtils.cs
- TextEndOfSegment.cs
- CrossSiteScriptingValidation.cs
- HtmlInputText.cs
- CacheModeValueSerializer.cs
- TransactionState.cs
- AppDomain.cs
- ConfigurationSectionHelper.cs
- Currency.cs
- UnhandledExceptionEventArgs.cs
- XmlCharCheckingWriter.cs
- FileLoadException.cs
- TextRangeAdaptor.cs
- XPathAncestorIterator.cs