PackagingUtilities.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Shared / MS / Internal / IO / Packaging / PackagingUtilities.cs / 1305600 / PackagingUtilities.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
// 
// History:
//  05/13/2004: [....]   Creation 
//
//---------------------------------------------------------------------------

using System; 
using System.IO;
using System.IO.IsolatedStorage; 
using MS.Internal.WindowsBase;  // FriendAccessAllowed 
using System.Xml;               // For XmlReader
using System.Diagnostics;       // For Debug.Assert 
using System.Text;              // For Encoding
using System.Windows;           // For Exception strings - SRID
using System.Security;                  // for SecurityCritical
using System.Security.Permissions;      // for permissions 
using Microsoft.Win32;                  // for Registry classes
 
 
using MS.Internal;
 
namespace MS.Internal.IO.Packaging
{
    [FriendAccessAllowed] // Built into Base, used by Framework and Core
    internal static class PackagingUtilities 
    {
        //----------------------------------------------------- 
        // 
        //  Internal Fields
        // 
        //-----------------------------------------------------
        internal static readonly string RelationshipNamespaceUri = "http://schemas.openxmlformats.org/package/2006/relationships";
        internal static readonly ContentType RelationshipPartContentType
            = new ContentType("application/vnd.openxmlformats-package.relationships+xml"); 

        internal const string ContainerFileExtension = "xps"; 
        internal const string XamlFileExtension = "xaml"; 

        //------------------------------------------------------ 
        //
        //  Internal Properties
        //
        //----------------------------------------------------- 

        //------------------------------------------------------ 
        // 
        //  Internal Methods
        // 
        //------------------------------------------------------

        #region Internal Methods
 
        /// 
        /// This method is used to determine if we support a given Encoding as per the 
        /// OPC and XPS specs. Currently the only two encodings supported are UTF-8 and 
        /// UTF-16 (Little Endian and Big Endian)
        ///  
        /// XmlTextReader
        /// throws an exception if the encoding is not UTF-8 or UTF-16
        internal static void PerformInitailReadAndVerifyEncoding(XmlTextReader reader)
        { 
            Invariant.Assert(reader != null && reader.ReadState == ReadState.Initial);
 
            //If the first node is XmlDeclaration we check to see if the encoding attribute is present 
            if (reader.Read() && reader.NodeType == XmlNodeType.XmlDeclaration && reader.Depth == 0)
            { 
                string encoding;
                encoding = reader.GetAttribute(_encodingAttribute);

                if (encoding != null && encoding.Length > 0) 
                {
                    encoding = encoding.ToUpperInvariant(); 
 
                    //If a non-empty encoding attribute is present [for example - ]
                    //we check to see if the value is either "utf-8" or utf-16. Only these two values are supported 
                    //Note: For Byte order markings that require additional information to be specified in
                    //the encoding attribute in XmlDeclaration have already been ruled out by this check as we allow for
                    //only two valid values.
                    if (String.CompareOrdinal(encoding, _webNameUTF8) == 0 
                        || String.CompareOrdinal(encoding, _webNameUnicode) == 0)
                        return; 
                    else 
                        //if the encoding attribute has any other value we throw an exception
                        throw new FileFormatException(SR.Get(SRID.EncodingNotSupported)); 
                }
            }

            //if the XmlDeclaration is not present, or encoding attribute is not present, we 
            //base our decision on byte order marking. reader.Encoding will take that into account
            //and return the correct value. 
            //Note: For Byte order markings that require additional information to be specified in 
            //the encoding attribute in XmlDeclaration have already been ruled out by the check above.
            //Note: If not encoding attribute is present or no byte order marking is present the 
            //encoding default to UTF8
            if (!(reader.Encoding is UnicodeEncoding || reader.Encoding is UTF8Encoding))
                throw new FileFormatException(SR.Get(SRID.EncodingNotSupported));
        } 

        ///  
        /// VerifyStreamReadArgs 
        /// 
        /// stream 
        /// buffer
        /// offset
        /// count
        /// Common argument verification for Stream.Read() 
        static internal void VerifyStreamReadArgs(Stream s, byte[] buffer, int offset, int count)
        { 
            if (!s.CanRead) 
                throw new NotSupportedException(SR.Get(SRID.ReadNotSupported));
 
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            } 

            if (offset < 0) 
            { 
                throw new ArgumentOutOfRangeException("offset", SR.Get(SRID.OffsetNegative));
            } 

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count", SR.Get(SRID.ReadCountNegative)); 
            }
 
            checked     // catch any integer overflows 
            {
                if (offset + count > buffer.Length) 
                {
                    throw new ArgumentException(SR.Get(SRID.ReadBufferTooSmall), "buffer");
                }
            } 
        }
 
        ///  
        /// VerifyStreamWriteArgs
        ///  
        /// 
        /// 
        /// 
        ///  
        /// common argument verification for Stream.Write
        static internal void VerifyStreamWriteArgs(Stream s, byte[] buffer, int offset, int count) 
        { 
            if (!s.CanWrite)
                throw new NotSupportedException(SR.Get(SRID.WriteNotSupported)); 

            if (buffer == null)
            {
                throw new ArgumentNullException("buffer"); 
            }
 
            if (offset < 0) 
            {
                throw new ArgumentOutOfRangeException("offset", SR.Get(SRID.OffsetNegative)); 
            }

            if (count < 0)
            { 
                throw new ArgumentOutOfRangeException("count", SR.Get(SRID.WriteCountNegative));
            } 
 
            checked
            { 
                if (offset + count > buffer.Length)
                    throw new ArgumentException(SR.Get(SRID.WriteBufferTooSmall), "buffer");
            }
        } 

        ///  
        /// Read utility that is guaranteed to return the number of bytes requested 
        /// if they are available.
        ///  
        /// stream to read from
        /// buffer to read into
        /// offset in buffer to write to
        /// bytes to read 
        /// bytes read
        /// Normal Stream.Read does not guarantee how many bytes it will 
        /// return.  This one does. 
        internal static int ReliableRead(Stream stream, byte[] buffer, int offset, int count)
        { 
            return ReliableRead(stream, buffer, offset, count, count);
        }

        ///  
        /// Read utility that is guaranteed to return the number of bytes requested
        /// if they are available. 
        ///  
        /// stream to read from
        /// buffer to read into 
        /// offset in buffer to write to
        /// count of bytes that we would like to read (max read size to try)
        /// minimal count of bytes that we would like to read (min read size to achieve)
        /// bytes read 
        /// Normal Stream.Read does not guarantee how many bytes it will
        /// return.  This one does. 
        internal static int ReliableRead(Stream stream, byte[] buffer, int offset, int requestedCount, int requiredCount) 
        {
            Invariant.Assert(stream != null); 
            Invariant.Assert(buffer != null);
            Invariant.Assert(buffer.Length > 0);
            Invariant.Assert(offset >= 0);
            Invariant.Assert(requestedCount >= 0); 
            Invariant.Assert(requiredCount >= 0);
            Invariant.Assert(checked(offset + requestedCount <= buffer.Length)); 
            Invariant.Assert(requiredCount <= requestedCount); 

            // let's read the whole block into our buffer 
            int totalBytesRead = 0;
            while (totalBytesRead < requiredCount)
            {
                int bytesRead = stream.Read(buffer, 
                                offset + totalBytesRead,
                                requestedCount - totalBytesRead); 
                if (bytesRead == 0) 
                {
                    break; 
                }
                totalBytesRead += bytesRead;
            }
            return totalBytesRead; 
        }
 
        ///  
        /// Read utility that is guaranteed to return the number of bytes requested
        /// if they are available. 
        /// 
        /// BinaryReader to read from
        /// buffer to read into
        /// offset in buffer to write to 
        /// bytes to read
        /// bytes read 
        /// Normal Stream.Read does not guarantee how many bytes it will 
        /// return.  This one does.
        internal static int ReliableRead(BinaryReader reader, byte[] buffer, int offset, int count) 
        {
            return ReliableRead(reader, buffer, offset, count, count);
        }
 
        /// 
        /// Read utility that is guaranteed to return the number of bytes requested 
        /// if they are available. 
        /// 
        /// BinaryReader to read from 
        /// buffer to read into
        /// offset in buffer to write to
        /// count of bytes that we would like to read (max read size to try)
        /// minimal count of bytes that we would like to read (min read size to achieve) 
        /// bytes read
        /// Normal Stream.Read does not guarantee how many bytes it will 
        /// return.  This one does. 
        internal static int ReliableRead(BinaryReader reader, byte[] buffer, int offset, int requestedCount, int requiredCount)
        { 
            Invariant.Assert(reader != null);
            Invariant.Assert(buffer != null);
            Invariant.Assert(buffer.Length > 0);
            Invariant.Assert(offset >= 0); 
            Invariant.Assert(requestedCount >= 0);
            Invariant.Assert(requiredCount >= 0); 
            Invariant.Assert(checked(offset + requestedCount <= buffer.Length)); 
            Invariant.Assert(requiredCount <= requestedCount);
 
            // let's read the whole block into our buffer
            int totalBytesRead = 0;
            while (totalBytesRead < requiredCount)
            { 
                int bytesRead = reader.Read(buffer,
                                offset + totalBytesRead, 
                                requestedCount - totalBytesRead); 
                if (bytesRead == 0)
                { 
                    break;
                }
                totalBytesRead += bytesRead;
            } 
            return totalBytesRead;
        } 
 
        /// 
        /// CopyStream utility that is guaranteed to return the number of bytes copied (may be less then requested, 
        /// if source stream doesn't have enough data)
        /// 
        /// stream to read from
        /// stream to write to  
        /// number of bytes to be copied(use Int64.MaxValue if the whole stream needs to be copied)
        /// number of bytes to be copied (usually it is 4K for scenarios where we expect a lot of data 
        ///  like in SparseMemoryStream case it could be larger  
        /// bytes copied (might be less than requested if source stream is too short
        /// Neither source nor target stream are seeked; it is up to the caller to make sure that their positions are properly set. 
        ///  Target stream isn't truncated even if it has more data past the area that was copied.
        internal static long CopyStream(Stream sourceStream, Stream targetStream, long bytesToCopy, int bufferSize)
        {
            Invariant.Assert(sourceStream != null); 
            Invariant.Assert(targetStream != null);
            Invariant.Assert(bytesToCopy >= 0); 
            Invariant.Assert(bufferSize > 0); 

            byte[] buffer = new byte[bufferSize]; 

            // let's read the whole block into our buffer
            long bytesLeftToCopy = bytesToCopy;
            while (bytesLeftToCopy > 0) 
            {
                int bytesRead = sourceStream.Read(buffer, 0, (int)Math.Min(bytesLeftToCopy, (long)bufferSize)); 
                if (bytesRead == 0) 
                {
                    targetStream.Flush(); 
                    return bytesToCopy - bytesLeftToCopy;
                }

                targetStream.Write(buffer, 0, bytesRead); 
                bytesLeftToCopy -= bytesRead;
            } 
 
            // It must not be negative
            Debug.Assert(bytesLeftToCopy == 0); 

            targetStream.Flush();
            return bytesToCopy;
        } 

 
        ///  
        /// Create a User-Domain Scoped IsolatedStorage file (or Machine-Domain scoped file if current user has no profile)
        ///  
        /// returns the created file name
        /// number of times to retry in case of name collision (legal values between 0 and 100)
        /// the created stream
        /// retryCount was exceeded 
        /// This function locks on IsoStoreSyncRoot and is thread-safe
        internal static Stream CreateUserScopedIsolatedStorageFileStreamWithRandomName(int retryCount, out String fileName) 
        { 
            // negative is illegal and place an upper limit of 100
            if (retryCount < 0 || retryCount > 100) 
                throw new ArgumentOutOfRangeException("retryCount");

            Stream s = null;
            fileName = null; 

            // GetRandomFileName returns a very random name, but collisions are still possible so we 
            // retry if we encounter one. 
            while (true)
            { 
                try
                {
                    // This function returns a highly-random name in 8.3 format.
                    fileName = Path.GetRandomFileName(); 

                    lock (IsoStoreSyncRoot) 
                    { 
                        s = GetDefaultIsolatedStorageFile().GetStream(fileName);
                    } 

                    // if we get to here we have a success condition so we can safely exit
                    break;
                } 
                catch (IOException)
                { 
                    // assume it is a name collision and ignore if we have not exhausted our retry count 
                    if (--retryCount < 0)
                        throw; 
                }
            }

            return s; 
        }
 
        ///  
        /// Calculate overlap between two blocks, returning the offset and length of the overlap
        ///  
        /// 
        /// 
        /// 
        ///  
        /// 
        ///  
        internal static void CalculateOverlap(long block1Offset, long block1Size, 
                                              long block2Offset, long block2Size,
                                              out long overlapBlockOffset, out long overlapBlockSize) 
        {
            checked
            {
                overlapBlockOffset = Math.Max(block1Offset, block2Offset); 
                overlapBlockSize = Math.Min(block1Offset + block1Size, block2Offset + block2Size) - overlapBlockOffset;
 
                if (overlapBlockSize <= 0) 
                {
                    overlapBlockSize = 0; 
                }
            }
        }
 
        /// 
        /// This method returns the count of xml attributes other than: 
        /// 1. xmlns="namespace" 
        /// 2. xmlns:someprefix="namespace"
        /// Reader should be positioned at the Element whose attributes 
        /// are to be counted.
        /// 
        /// 
        /// An integer indicating the number of non-xmlns attributes 
        internal static int GetNonXmlnsAttributeCount(XmlReader reader)
        { 
            Debug.Assert(reader != null, "xmlReader should not be null"); 
            Debug.Assert(reader.NodeType == XmlNodeType.Element, "XmlReader should be positioned at an Element");
 
            int readerCount = 0;

            //If true, reader moves to the attribute
            //If false, there are no more attributes (or none) 
            //and in that case the position of the reader is unchanged.
            //First time through, since the reader will be positioned at an Element, 
            //MoveToNextAttribute is the same as MoveToFirstAttribute. 
            while (reader.MoveToNextAttribute())
            { 
                if (String.CompareOrdinal(reader.Name, XmlNamespace) != 0 &&
                    String.CompareOrdinal(reader.Prefix, XmlNamespace) != 0)
                    readerCount++;
            } 

            //re-position the reader to the element 
            reader.MoveToElement(); 

            return readerCount; 
        }

        /// 
        /// Any usage of IsolatedStorage static properties should lock on this for thread-safety 
        /// 
        internal static Object IsoStoreSyncRoot 
        { 
            get
            { 
                return _isoStoreSyncObject;
            }
        }
 
        #endregion Internal Methods
 
        //----------------------------------------------------- 
        //
        //  Private Methods 
        //
        //------------------------------------------------------

        ///  
        /// Delete file created using CreateUserScopedIsolatedStorageFileStreamWithRandomName()
        ///  
        ///  
        /// Correctly handles temp/isostore differences
        private static void DeleteIsolatedStorageFile(String fileName) 
        {
            lock (IsoStoreSyncRoot)
            {
                GetDefaultIsolatedStorageFile().IsoFile.DeleteFile(fileName); 
            }
        } 
 

        ///  
        /// Returns the IsolatedStorageFile scoped to Assembly, Domain and User
        /// 
        /// Callers must lock on IsoStoreSyncRoot before calling this for thread-safety.
        /// For example: 
        ///
        ///   lock (IsoStoreSyncRoot) 
        ///   { 
        ///       // do something with the returned IsolatedStorageFile
        ///       PackagingUtilities.DefaultIsolatedStorageFile.DeleteFile(_isolatedStorageStreamFileName); 
        ///   }
        ///
        ///
        private static ReliableIsolatedStorageFileFolder GetDefaultIsolatedStorageFile() 
        {
            // Cache and re-use the same object for multiple requests - resurrect if disposed 
            if (_defaultFile == null || _defaultFile.IsDisposed()) 
            {
                _defaultFile = new ReliableIsolatedStorageFileFolder(); 
            }

            return _defaultFile;
        } 

 
        /// 
        /// Determine if current user has a User Profile so we can determine the appropriate
        /// scope to use for IsolatedStorage functionality. 
        ///
        ///
        /// Critical - Asserts read registry permission...
        ///          - Asserts ControlPrincipal to access current user identity 
        /// TAS - only returns a bool
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private static bool UserHasProfile()
        { 

            // Acquire permissions to read the one key we care about from the registry
            // Acquite permission to query the current user identity
            PermissionSet permissionSet = new PermissionSet(PermissionState.None); 
            permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.ControlPrincipal));
            permissionSet.AddPermission(new RegistryPermission(RegistryPermissionAccess.Read, 
                _fullProfileListKeyName)); 
            permissionSet.Assert();
 
            bool userHasProfile = false;
            RegistryKey userProfileKey = null;
            try
            { 
                // inspect registry and look for user profile via SID
                string userSid = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value; 
                userProfileKey = Registry.LocalMachine.OpenSubKey(_profileListKeyName + @"\" + userSid); 
                userHasProfile = userProfileKey != null;
            } 
            finally
            {
                if (userProfileKey != null)
                    userProfileKey.Close(); 

                CodeAccessPermission.RevertAssert(); 
            } 

            return userHasProfile; 
        }

        //------------------------------------------------------
        // 
        //  Private Classes
        // 
        //------------------------------------------------------ 

        ///  
        /// This class extends IsolatedStorageFileStream by adding a finalizer to ensure that
        /// the underlying file is deleted when the stream is closed.
        /// 
        private class SafeIsolatedStorageFileStream : IsolatedStorageFileStream 
        {
            //------------------------------------------------------ 
            // 
            //  Internal Methods
            // 
            //------------------------------------------------------
            internal SafeIsolatedStorageFileStream(
                string path, FileMode mode, FileAccess access,
                FileShare share, ReliableIsolatedStorageFileFolder folder) 
                : base(path, mode, access, share, folder.IsoFile)
            { 
                if (path == null) 
                    throw new ArgumentNullException("path");
 
                _path = path;
                _folder = folder;
                _folder.AddRef();
            } 

            //------------------------------------------------------ 
            // 
            //  Protected Methods
            // 
            //------------------------------------------------------
            protected override void Dispose(bool disposing)
            {
                if (!_disposed) 
                {
                    if (disposing) 
                    { 
                        // Non-standard pattern - call base.Dispose() first.
                        // This is required because the base class is a stream and we cannot 
                        // delete the underlying file storage before it has a chance to close
                        // and release it.
                        base.Dispose(disposing);
 
                        if (_path != null)
                        { 
                            PackagingUtilities.DeleteIsolatedStorageFile(_path); 
                            _path = null;
                        } 

                        //Decrement the count of files
                        _folder.DecRef();
                        _folder = null; 
                        GC.SuppressFinalize(this);
                    } 
                    _disposed = true; 
                }
            } 

            //------------------------------------------------------
            //
            //  Private Fields 
            //
            //------------------------------------------------------ 
            private string _path; 
            private ReliableIsolatedStorageFileFolder _folder;
            private bool   _disposed; 
        }


 
        /// 
        /// This class extends IsolatedStorageFileStream by adding a finalizer to ensure that 
        /// the underlying file is deleted when the stream is closed. 
        /// 
        private class ReliableIsolatedStorageFileFolder : IDisposable 
        {
            //------------------------------------------------------
            //
            //  Internal Properties 
            //
            //------------------------------------------------------ 
            internal IsolatedStorageFile IsoFile 
            {
                get 
                {
                    CheckDisposed();
                    return _file;
                } 
            }
 
            ///  
            /// Call this when a new file is created in the isoFolder
            ///  
            internal void AddRef()
            {
                lock (IsoStoreSyncRoot)
                { 
                    CheckDisposed();
                    checked 
                    { 
                        ++_refCount;
                    } 
                }
            }

            ///  
            /// Call this when a new file is deleted from the isoFolder
            ///  
            internal void DecRef() 
            {
                lock (IsoStoreSyncRoot) 
                {
                    CheckDisposed();
                    checked
                    { 
                        --_refCount;
                    } 
                    if (_refCount <= 0) 
                    {
                        Dispose(); 
                    }
                }
            }
 
            /// 
            /// Only used within a lock statement 
            ///  
            /// 
            internal bool IsDisposed() 
            {
                return _disposed;
            }
 
            //------------------------------------------------------
            // 
            //  Internal Methods 
            //
            //------------------------------------------------------ 
            internal ReliableIsolatedStorageFileFolder()
            {
                _userHasProfile = UserHasProfile();
                _file = GetCurrentStore(); 
            }
 
            ///  
            /// This triggers AddRef because SafeIsoStream does this in its constructor
            ///  
            /// 
            /// 
            internal Stream GetStream(String fileName)
            { 
                CheckDisposed();
 
                // This constructor uses a scope that isolates by AppDomain and User 
                // We cannot include Assembly scope because it prevents sharing between Base and Core dll's
                return new SafeIsolatedStorageFileStream( 
                    fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None,
                    this);
            }
 
            /// 
            /// IDisposable.Dispose() 
            ///  
            public void Dispose()
            { 
                Dispose(true);
            }

            //------------------------------------------------------ 
            //
            //  Protected Methods 
            // 
            //------------------------------------------------------
            protected virtual void Dispose(bool disposing) 
            {
                try
                {
                    // only lock if we are disposing 
                    if (disposing)
                    { 
                        lock (IsoStoreSyncRoot) 
                        {
                            if (!_disposed) 
                            {
                                using (_file)
                                {
                                    _file.Remove(); 
                                }
                                _disposed = true; 
                            } 
                            _file = null;
                        } 
                        GC.SuppressFinalize(this);
                    }
                    else
                    { 
                        // We cannot rely on other managed objects in our finalizer
                        // so we allocate a fresh object to help us delete our temp folder. 
                        using (IsolatedStorageFile file = GetCurrentStore()) 
                        {
                            file.Remove(); 
                        }
                    }
                }
                catch (IsolatedStorageException) 
                {
                    // IsolatedStorageException can be thrown if the files that are being deleted, are 
                    // currently in use. These files will not get cleaned up. 
                }
            } 

            //------------------------------------------------------
            //
            //  Private Methods 
            //
            //------------------------------------------------------ 
            ///  
            /// Call this sparingly as it allocates resources
            ///  
            /// 
            private IsolatedStorageFile GetCurrentStore()
            {
                if (_userHasProfile) 
                {
                    return IsolatedStorageFile.GetUserStoreForDomain(); 
                } 
                else
                { 
                    return IsolatedStorageFile.GetMachineStoreForDomain();
                }
            }
 
            ~ReliableIsolatedStorageFileFolder()
            { 
                Dispose(false); 
            }
 
            void CheckDisposed()
            {
                if (_disposed)
                    throw new ObjectDisposedException("ReliableIsolatedStorageFileFolder"); 
            }
 
            //------------------------------------------------------ 
            //
            //  Private Fields 
            //
            //------------------------------------------------------
            private static IsolatedStorageFile _file;
            private static bool                _userHasProfile; 
            private int                        _refCount;               // number of outstanding "streams"
            private bool                       _disposed; 
        } 

        //------------------------------------------------------ 
        //
        //  Private Fields
        //
        //------------------------------------------------------ 
        /// 
        /// Synchronize access to IsolatedStorage methods that can step on each-other 
        ///  
        /// See PS 1468964 for details.
        private static Object _isoStoreSyncObject = new Object(); 
        private static ReliableIsolatedStorageFileFolder _defaultFile;
        private const string XmlNamespace = "xmlns";
        private const string _encodingAttribute = "encoding";
        private static readonly string _webNameUTF8 = Encoding.UTF8.WebName.ToUpperInvariant(); 
        private static readonly string _webNameUnicode = Encoding.Unicode.WebName.ToUpperInvariant();
 
        ///  
        /// ProfileListKeyName
        ///  
        ///
        /// _profileListKeyName must remain readonly for security reasons
        ///
        private const string _profileListKeyName = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"; 
        private const string _fullProfileListKeyName = @"HKEY_LOCAL_MACHINE\" + _profileListKeyName;
    } 
} 

// 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